android自定义布局——城市选择界面

以前做的项目需要用到这样的一个界面,先发一张效果图,个人感觉还算可以,下面把我的实现过程记录一下。


首先,数据格式是这样的


很明显这个界面整体是一个listview,每个省是一个listitem,刚开始实现方法是在adapter的getview里动态的在每个listitem里添加LinearLayout,每一行城市就是一个LinearLayout,通过判断当前的城市的索引决定是否要换行,即新建一个LinearLayout。后来觉得这样实现不太好看,代码太乱了,于是就想到了用自定义View,原理跟原来的差不多,也是利用当前城市的索引决定是否换行。

直接上源码,

package cn.hnsi.android.apps.smartlife.ui.widget;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

/**
 * 显示城市名称
 * @author LiChaofei 
 * <br/>2014-3-24 下午3:03:21
 */
public class CitiesLayout extends ViewGroup {
private static final String TAG="CitiesLayout";
private static final int COLUMN_COUNT=5;
private static final int HORIZONTAL_SPACE=2;
private static final int VERTICAL_SPACE=5;
private int maxChildWidth=0;
private int maxChildHeight=0;

	public CitiesLayout(Context context) {
		super(context);
	}
	

	public CitiesLayout(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		// TODO Auto-generated constructor stub
	}


	public CitiesLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}


	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		int widthSize=MeasureSpec.getSize(widthMeasureSpec);
		int heightSize=MeasureSpec.getSize(heightMeasureSpec);
		int paddingLeft=this.getPaddingLeft();
		int paddingRight=this.getPaddingTop();
		int paddingTop=this.getPaddingTop();
		int paddingBottom=this.getPaddingBottom();
		
//		Log.d(TAG, "调用onMeasure,width="+widthSize+",height="+heightSize+",paddingLeft="+paddingLeft+",paddingRight="+paddingRight+",paddingTop="+paddingTop+",paddingBottom="+paddingBottom);
		
		
		//类似9宫格的形式
		maxChildHeight=maxChildWidth=(widthSize-paddingLeft-paddingRight)/COLUMN_COUNT-HORIZONTAL_SPACE*2;
		
		int childMeasureWidthSpec=MeasureSpec.makeMeasureSpec(maxChildWidth, MeasureSpec.EXACTLY);
		int childMeasureHeightSpec=MeasureSpec.makeMeasureSpec(maxChildHeight, MeasureSpec.EXACTLY);
		
		int childCount = getChildCount();
		for (int index = 0; index < childCount; index++) {
		final View child = getChildAt(index);
		// measure
		child.measure(childMeasureWidthSpec, childMeasureHeightSpec);
	}
		

		int rowCount=childCount%COLUMN_COUNT==0?childCount/COLUMN_COUNT:childCount/COLUMN_COUNT+1;
		heightSize=(maxChildHeight+VERTICAL_SPACE*2)*rowCount+paddingTop+paddingBottom;
		heightMeasureSpec=MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
		
		setMeasuredDimension(
				resolveSize(widthSize, widthMeasureSpec), 
				resolveSize(heightSize, heightMeasureSpec));
	}


	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
//		Log.d(TAG, "maxChildWidth="+maxChildWidth);
		int paddingLeft=this.getPaddingLeft();
		int paddingTop=this.getPaddingTop();
		int total=getChildCount();
		for(int i=0;i<total;i++){
			final View child=getChildAt(i);
			int row=i/COLUMN_COUNT;
			int colomn=i%COLUMN_COUNT;
			
			int left =paddingLeft+(maxChildWidth+HORIZONTAL_SPACE*2)*colomn+HORIZONTAL_SPACE;
			int top = paddingTop+(maxChildHeight+VERTICAL_SPACE*2)*row+VERTICAL_SPACE;
//			Log.d(TAG, "left="+left+",top="+top);
			child.layout(left, top, left+maxChildWidth, top+maxChildHeight);
		}
	}

}

Adapter的源码

protected class CityAdapter extends BaseAdapter {
		Context mContext;
		LayoutInflater inflater;
		List<ProvinceEntity> dataList;
//		int unitWidth;

		public CityAdapter(Context context, List<ProvinceEntity> datas) {
			mContext = context;
			inflater = (LayoutInflater) context
					.getSystemService(LAYOUT_INFLATER_SERVICE);
			dataList = datas;
//			DisplayMetrics metrics = new DisplayMetrics();
//			getWindowManager().getDefaultDisplay().getMetrics(metrics);
//			unitWidth=(metrics.widthPixels-5*6)/5;
		}

		@Override
		public int getCount() {
			return dataList != null ? dataList.size() : 0;
		}

		@Override
		public Object getItem(int position) {
			return dataList.get(position);
		}

		@Override
		public long getItemId(int position) {
			return position;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {

			convertView = inflater.inflate(R.layout.list_item_city, parent,false);

			TextView provinceName = (TextView) convertView
					.findViewById(android.R.id.title);
			ProvinceEntity province = dataList.get(position);
			provinceName.setText(province.name);

			List<CityEntity> cities = province.cities;
			CitiesLayout container=(CitiesLayout) convertView.findViewById(R.id.city_container);
			for (int i = 0, len = cities.size(); i < len; i++) {
				
				CityEntity city=cities.get(i);
				TextView cityName=createTextView(city);
				container.addView(cityName);
			}

			return convertView;
		}

		/**
		 * 创建一个TextView
		 * @author LiChaofei
		 * <br/>2013-12-10 下午2:48:59
		 * @param city TODO
		 * @return
		 */
		private TextView createTextView(final CityEntity city) {
			final TextView view=new TextView(mContext);
			LayoutParams params=new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
//			params.leftMargin=2;
//			params.rightMargin=2;
			view.setLayoutParams(params);
//			view.setPadding(10, 10, 10, 10);
			view.setTextColor(Color.BLACK);
			view.setBackgroundResource(R.drawable.bg_city_selector);
			view.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
			view.setGravity(Gravity.CENTER);
			view.setText(city.name);
			view.setTag(city.cityId);
			view.setOnClickListener(new View.OnClickListener() {
				
				@Override
				public void onClick(View v) {
					intent.putExtra(WeatherActivity.CITY_ID, city.cityId);
					intent.putExtra(WeatherActivity.CITY_NAME, city.name);
					setResult(RESULT_OK, intent);
					finish();
				}
			});
			return view;
		}

	}
用到的bg_city_selector

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_pressed="true">
        <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item>
        <shape android:shape="rectangle" >
            <solid android:color="@color/black" />

            <corners android:radius="5dp" />

            <stroke
                android:width="1dip"
                android:color="@color/black" />
        </shape>
    </item>
    <item
        android:bottom="2px"
        android:left="0px"
        android:right="0px">
        <shape android:shape="rectangle" >
            <gradient
                android:angle="90"
                android:endColor="#cccccc"
                android:startColor="#e1e1e1" />

            <corners android:radius="5dp" />

            <stroke
                android:width="0dip"
                android:color="@color/black" />
        </shape>
    </item>

</layer-list>
    </item>
    <!-- 普通状态 -->
    <item><layer-list xmlns:android="http://schemas.android.com/apk/res/android">
            <item><shape android:shape="rectangle">
                    <solid android:color="@color/black" />

                    <corners android:radius="5dp" />

                    <stroke android:width="1dip" android:color="@color/black" />
                </shape></item>
            <item android:bottom="2px" android:left="0px" android:right="0px"><shape android:shape="rectangle">
                    <gradient android:angle="90" android:endColor="#e1e1e1" android:startColor="#cccccc" />

                    <corners android:radius="5dp" />

                    <stroke android:width="0dip" android:color="@color/black" />
                </shape></item>
        </layer-list></item>

</selector>

代码比较简单,我就不多说了,如果有不对的地方,欢迎批评指正。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值