Android自定义控件继承ViewGrop实现一个GridView的显示效果

本文介绍了如何在Android中通过继承ViewGroup类来创建一个自定义控件,实现类似GridView的显示效果。重点探讨了View类和ViewGroup类的基本概念及其在自定义控件中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 View类和ViewGroup类介绍

 android中所有的UI控件(button,textView,checkbox等等)基类都View类,android中的五种布局(LinearLayout ,RelativeLayout ,FrameLayout ,TableLayout ,AbsoluteLayout)都是View的容器,我们平常在XML文件中写的布局,定义的那些View都是需要VIew容器去包装它们,包括View控件之间的排版,控件与控件之间的间距等。例如LinearLayout就是将你定义的那些VIew控件按照线性的布局方式去排版。

      ViewGroup类就是VIew容器的基类,LinearLayout ,RelativeLayout ,FrameLayout ,TableLayout ,AbsoluteLayout都是继承与ViewGroup。所以我们可以写一个自己的类去继承ViewGroup,这样我们的类就是一个View的容器了。我们写的UI控件就可以定义在我们自己的View容器中。

本文的例子就是继承于ViewGroup实现一个GridView排版效果的容器。
继承ViewGroup类主要是要覆写它的  onMeasure(),onLayout()这两个方法,onMeasure()方法用于测量每个子View的大小尺寸,onLayout()方法用于确定子View在布局中的位置。

DEMO:
<pre name="code" class="java">package cn.mmb.view;

import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;

import java.util.Iterator;

import com.mmb.demo.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

public class MyGridLayout extends ViewGroup {

	public final String TAG = "MyGridLayout";
	public static int DEFAULT_COLUMS = 2;
	public static int DEFAULT_HORIZONTAL_SPACING = 20;
	public static int DEFAULT_VERTICAL_SPACING = 20;
	public static int DEFAULT_ITEM_HEIGHT = 40;
	private int mHorizontalSpacing = 0;
	private int mVerticalSpacing = 0;
	private int colums = DEFAULT_COLUMS;
	private int mMaxChildWidth = 0;
	private int mMaxChildHeight = 0;
	private int count = 0;
	private int mItemHeight = 0;
	private GridAdatper adapter;

	public MyGridLayout(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		if (attrs != null) {
			TypedArray typedArray = getContext().obtainStyledAttributes(attrs,
					R.styleable.MyGridLayout);
			colums = typedArray.getInteger(R.styleable.MyGridLayout_numColumns,
					DEFAULT_COLUMS);
			mHorizontalSpacing = (int) typedArray.getInteger(
					R.styleable.MyGridLayout_horizontal_spacing,
					DEFAULT_HORIZONTAL_SPACING);
			mVerticalSpacing = (int) typedArray.getInteger(
					R.styleable.MyGridLayout_vertical_spacing,
					DEFAULT_VERTICAL_SPACING);
			mItemHeight = (int) typedArray.getInteger(
					R.styleable.MyGridLayout_itemHeight, DEFAULT_ITEM_HEIGHT);
		}
	}

	public MyGridLayout(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public MyGridLayout(Context context) {
		this(context, null);
	}

	/**
	 * @return item Horizontal Spacing
	 */
	public int getHorizontalSpacing() {
		return mHorizontalSpacing;
	}

	/**
	 * set item Horizontal Spacing
	 * 
	 * @param mHorizontalSpacing
	 */
	public void setHorizontalSpacing(int mHorizontalSpacing) {
		this.mHorizontalSpacing = mHorizontalSpacing;
	}

	/**
	 * @return item Vertical Spacing
	 */
	public int getVerticalSpacing() {
		return mVerticalSpacing;
	}

	/**
	 * set item Vertical Spacing
	 * 
	 * @param mVerticalSpacing
	 */
	public void setVerticalSpacing(int mVerticalSpacing) {
		this.mVerticalSpacing = mVerticalSpacing;
	}

	/**
	 * @return item height
	 */
	public int getItemHeight() {
		return mItemHeight;
	}

	/**
	 * set item height
	 * 
	 * @param mItemHeight
	 */
	public void setItemHeight(int mItemHeight) {
		this.mItemHeight = mItemHeight;
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

		mMaxChildWidth = 0;
		mMaxChildHeight = 0;

		int modeW = 0, modeH = 0;
		if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED) {
			modeW = MeasureSpec.UNSPECIFIED;
		}
		if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.UNSPECIFIED) {
			modeH = MeasureSpec.UNSPECIFIED;
		}
		final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
				MeasureSpec.getSize(widthMeasureSpec), modeW);
		final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
				MeasureSpec.getSize(heightMeasureSpec), modeH);

		count = getChildCount();
		if (count == 0) {
			super.onMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
			return;
		}
		for (int i = 0; i < count; i++) {
			final View child = getChildAt(i);
			if (child.getVisibility() == GONE) {
				continue;
			}

			child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
			mMaxChildWidth = Math.max(mMaxChildWidth, child.getMeasuredWidth());
			mMaxChildHeight = Math.max(mMaxChildHeight,
					child.getMeasuredHeight());
		}
		setMeasuredDimension(resolveSize(mMaxChildWidth, widthMeasureSpec),
				resolveSize(mMaxChildHeight, heightMeasureSpec));
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {

		int height = b - t; // The height of the layout area
		int width = r - l; // The width of the layout area
		// Calculate the number  of  rows
		int rows = count % colums == 0 ? count / colums : count / colums + 1;
		if (count == 0) {
			return;
		}
		// Calculate every item  width
		int gridW = (width - (colums + 1) * mHorizontalSpacing) / colums;
		// item height
		int gridH = mItemHeight; 
		// int gridH = (height - mVerticalSpacing * rows) / rows;
		int left = 0;
		int top = mVerticalSpacing;

		for (int i = 0; i < rows; i++) {
			// Iterator the elements of each row
			for (int j = 0; j < colums; j++) {
				View child = this.getChildAt(i * colums + j);
				if (child == null) {
					return;
				}
				left = j * gridW + j * mHorizontalSpacing + mHorizontalSpacing;

				/**
				 * If the current layout is not the same width and width
				 * measurements, the direct use of re-measuring the width of the
				 * current layout
				 */
				if (gridW != child.getMeasuredWidth()
						|| gridH != child.getMeasuredHeight()) {
					child.measure(makeMeasureSpec(gridW, EXACTLY),
							makeMeasureSpec(gridH, EXACTLY));
				}
				/**
				 * set current child view layout
				 */
				child.layout(left, top, left + gridW, top + gridH); 
			}
			top += gridH + mVerticalSpacing; // next rows
		}
	}

	public interface GridAdatper {
		View getView(int index);

		int getCount();
	}

	/**
	 * set adapter
	 * 
	 * @param adapter
	 */
	public void setGridAdapter(GridAdatper adapter) {
		this.adapter = adapter;
		int size = adapter.getCount();
		for (int i = 0; i < size; i++) {
			addView(adapter.getView(i)); // addView
		}
	}

	public interface OnItemClickListener {
		void onItemClick(View v, int index);
	}

	/**
	 * set Item Click Listener
	 * 
	 * @param click
	 */
	public void setOnItemClickListener(final OnItemClickListener click) {
		if (this.adapter == null)
			return;
		for (int i = 0; i < adapter.getCount(); i++) {
			final int index = i;
			View view = getChildAt(i);
			view.setOnClickListener(new View.OnClickListener() {

				@Override
				public void onClick(View v) {
					click.onItemClick(v, index);
				}
			});
		}
	}

}



布局(activity_main.xml)中使用已经写好的View容器
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#303030"
    android:orientation="vertical" >

    <cn.mmb.view.MyGridLayout
        android:id="@+id/list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="#1e1d1d"
        android:orientation="vertical"
        app:horizontal_spacing="30"
        app:vertical_spacing="20" 
        app:itemHeight="60"
        app:numColumns="3">
    </cn.mmb.view.MyGridLayout>

</LinearLayout>
最后在Activity中
package com.mmb.demo;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpResponse;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import cn.mmb.view.MyGridLayout;
import com.mmb.demo.domin.MyColor;
import com.mmb.demo.util.HttpUtil;
import com.mmb.demo.util.Utils;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

	private static final String MREQUESTPATH = "http://192.168.3.105:8080/demo/json.txt";
	private List<MyColor> mColorList;
	private MyGridLayout mGridLayout;

	@Override
	protected void onCreate(Bundle savedInstanceState) {

		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mColorList = new ArrayList<MyColor>();
		Utils.computeScreenDimen(this);

		mGridLayout = (MyGridLayout) findViewById(R.id.list);
		mGridLayout.setVerticalSpacing(Utils.uiHeightPxToScreenPx(30));
		mGridLayout.setHorizontalSpacing(Utils.uiWidthPxToScreenPx(40));
		mGridLayout.setItemHeight(Utils.uiHeightPxToScreenPx(100));

		new myWorkAsyncTask().execute();

	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	class myWorkAsyncTask extends AsyncTask<Void, Void, Void> {

		private static final String TAG = "myWorkAsyncTask";

		@Override
		protected Void doInBackground(Void... params) {

			String data = getJsonData(MREQUESTPATH);
			if (data != null) {
				parseJson(data);
			}
			return null;
		}

		private void parseJson(String data) {

			try {
				JSONObject jsonObject = new JSONObject(data);
				JSONArray jsonArray = jsonObject.getJSONArray("array");
				for (int i = 0; i < jsonArray.length(); i++) {
					JSONObject object = (JSONObject) jsonArray.get(i);
					int id = object.getInt("subId");
					String name = object.getString("name");
					mColorList.add(new MyColor(id, name));
// Log.i (TAG, name);
				}
			} catch (JSONException e) {
				e.printStackTrace();
			}
		}

		private String getJsonData(String mRequestPath) {

			HttpResponse response = null;
			BufferedReader lineReader = null;
			try {
				response = HttpUtil.getHttpResponse(mRequestPath);
				int rescode = response.getStatusLine().getStatusCode();
				if (rescode == 200) {
					String line = null;
					StringBuilder builder = new StringBuilder();
					lineReader = new BufferedReader(new InputStreamReader(
							response.getEntity().getContent()));
					while ((line = lineReader.readLine()) != null) {
						builder.append(line);
					}
					return builder.toString();
				}
			} catch (Exception e) {
			} finally {
				if (lineReader != null) {
					try {
						lineReader.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}

			}
			return null;
		}

		@Override
		protected void onPostExecute(Void result) {
			super.onPostExecute(result);

			mGridLayout
					.setGridAdapter(new cn.mmb.view.MyGridLayout.GridAdatper() {

						@Override
						public View getView(int index) {
							View view = getLayoutInflater().inflate(
									R.layout.actions_item, null);
							TextView tv = (TextView) view.findViewById(R.id.tv);
							tv.setText(mColorList.get(index).getName());
							return view;
						}
						@Override
						public int getCount() {
							return mColorList.size();
						}
					});

			mGridLayout
					.setOnItemClickListener(new cn.mmb.view.MyGridLayout.OnItemClickListener() {
						@Override
						public void onItemClick(View v, int index) {
							Toast.makeText(getApplicationContext(),
									"item=" + index, Toast.LENGTH_SHORT).show();
						}
					});
		}

	}

}
运行结果:其子View item的排列效果类似于GridView。



  
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值