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。