完美移植Android4.0中的spinner控件用于低版本的系统

http://blog.csdn.net/hello_haozi/article/details/7619442

这篇博文给了我思路,我也吸收了他的一些想法和思路,最后实现完美移植

首先上运行效果图:





一开始做这个,是老板交代了的一个项目,当时需要做三级的数据联动,但是用三级列表给人一种小题大做的感觉,但是又用3个spinner感觉略显粗糙,最后定了前面两级用spinner,第三级数据采用一个listview............哎,免费劳动力辛苦啊,年底了,老板千万别忘了发点辛苦费啊!!→_→

所以开始做了以后,发现2.2的spinner真是丑的要死,但是4.0上的就相当漂亮(都是为了兼容低版本惹的祸啊),SO,开始萌生了移植4.0的控件到低版本的想法,说做就做,中间经过不停的查资料,发现上面的方法给的一个思路就相当不错,但是还是有细节没有处理,所以开始另起炉灶搞起。


移植的首要第一步,是把4.0系统中的贴图抠出来,呵呵,这里给大家一个笨办法。首先打开SDK目录的platforms文件夹(比如我的就是“E:\JAVA\Android-SDK\platforms”)


然后打开你要移植的版本号,定位到 \data\res 目录下即可


这回同志们知道该怎么干了吧,对头,就是打开“\res” 目录找到你需要的资源文件即可,(PS,这里面的android.jar也是可以反编译出来系统源码的,如果需要可自行百度)


如图所示的就是我们需要的贴图文件,复制出来留待后用即可(这里有多套配置,根据自己的需求来处理就行)。注意这里提取出来的图片直接就是9.png,我们无需再次处理。直接拿来使用




然后就是定义各个select资源。

my_spinner_list_set.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
	<item android:state_pressed="true" android:drawable="@drawable/spinner_ab_pressed_holo_light" />
	<item android:state_focused="true" android:state_pressed="false" android:drawable="@drawable/spinner_ab_pressed_holo_light" />
	<item android:drawable="@drawable/spinner_ab_default_holo_light" />
</selector>
这是MySpinner按钮的背景


spinner.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:state_pressed="true" android:drawable="@color/spinner_list_select" />
	<item android:state_focused="true" android:drawable="@color/spinner_list_select" />
	<item android:drawable="@android:color/transparent" />
</selector>

这是下拉列表的单个选项的select背景()这里面的颜色都是我一个个的测出来的。


接下来就是常规的新建工程,这里先要大概的描述一下具体的思路,这里的实现过程主要是首先继承一个button,做出spinner上的按钮来,当点击按钮是显示一个PopupWindow,同时要回调activity中的OnItemSelectedListener监听器即可。

首先新建一个MySpinner类继承自Button类。

代码我就直接贴出来了

MySpinner.java

package com.example.myspinner;

import java.util.ArrayList;

import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.TextView;

/**
 * 自定义实现的spinner
 * @author A Shuai
 *
 */
public class MySpinner extends Button {

	private Context context = null;
	private OnItemSelectedListener listener = null;
	private ArrayList<String> data = null;
	private SpinnerDropDownPopupWindow dropDown = null;
	
	/**
	 * 构造方法
	 * @param context
	 */
	public MySpinner(Context context) {
		this(context, null);
	}
	
	public MySpinner(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}
	
	public MySpinner(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(context);
	}
	
	private void init( Context context ){
		this.context = context;
		data = new ArrayList<String>();
		setOnClickListener( new SpinnerButtonOnClickListener() );
	}
	
	/**
	 * 设置spinner的监听器,用于回调,务必在显示下拉列表前载入
	 * @param listener
	 */
	public void setOnItemSelectedListener( OnItemSelectedListener listener ){
		this.listener = listener;
	}
	
	/**
	 * 这个方法在显示前调用,装载入数据
	 * @param data
	 */
	public void setData( ArrayList<String> data ){
		this.data = data;
	}
	
	class SpinnerButtonOnClickListener implements OnClickListener{

		public void onClick(View v) {
			if(dropDown == null){
				dropDown = new SpinnerDropDownPopupWindow(context);
			}
			if(!dropDown.isShowing()){
				dropDown.showAsDropDown(MySpinner.this);
			}
		}
		
	}
	
	class SpinnerDropDownPopupWindow extends PopupWindow{
		
		private LayoutInflater inflater = null;
		
		private ListView listView = null;
		
		private SpinnerDropdownAdapter adapter = null;
		
		public SpinnerDropDownPopupWindow( Context context ){
			super(context);
			inflater = LayoutInflater.from( context );
			
			adapter = new SpinnerDropdownAdapter();
			
			View view = inflater.inflate(R.layout.my_spinner, null);
			listView = (ListView)view.findViewById(R.id.my_spinner_list);
			listView.setAdapter(adapter);
			listView.setOnItemClickListener( new SpinnerListOnItemClickListener() );
			
			setWidth(MySpinner.this.getLayoutParams().width);
			setHeight(LayoutParams.WRAP_CONTENT);
			// 这个是为了点击“返回Back”也能使其消失,并且并不会影响你的背景
			setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
			setFocusable(true);		//得到焦点
			setOutsideTouchable(true);	//点击布局外失去焦点
			setContentView(view);
		}
		
		
		
		public void showAsDropDown(View view) {
			showAsDropDown(view, 0, 0);
			update();		//刷新
		}



		/**
		 * 适配器
		 */
		private final class SpinnerDropdownAdapter extends BaseAdapter {

			public int getCount() {
				return data.size();
			}

			public Object getItem(int position) {
				return data.get(position);
			}

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

			public View getView(int position, View convertView, ViewGroup parent) {
				ViewHolder holder = null;
				if (convertView == null) {
					holder = new ViewHolder();
					convertView = inflater.inflate(R.layout.my_spinner_item, null);
					holder.txt = (TextView) convertView.findViewById(R.id.my_spinner_item_text);
					convertView.setTag(holder);
				} else {
					holder = (ViewHolder) convertView.getTag();
				}
				holder.txt.setText(data.get(position));
				
				return convertView;
			}
			
		}
		
		/**
		 * holder类
		 */
		private final class ViewHolder {
			TextView txt;
		}
		
		class SpinnerListOnItemClickListener implements OnItemClickListener{

			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
				TextView mTextView = (TextView) view.findViewById(R.id.my_spinner_item_text);
				String content = mTextView.getText().toString();
				MySpinner.this.setText(content);
				listener.onItemSelected(parent, view, position, id);
				SpinnerDropDownPopupWindow.this.dismiss();
			}
			
		}

	}

}

这里面有两个特殊的方法,一个是setOnItemSelectedListener( OnItemSelectedListener listener ),这里就是模拟spinner对象绑定OnItemSelectedListener 监听器,用于后面调用;另一个方法是setData( ArrayList<String> data ),这个方法是给下拉列表填充数据用的。所以在你需要显示前应该首先填充数据,这里为了处理方便,我已经提前实例化了ArrayList<String> data对象,这是为了防止报空指针异常。

接下来,是定义一个OnClickListener监听器并给MyPinner按钮绑定用的,也就是说当点击按钮时是如果PopupWindow没显示,则显示,为什么没有判定另一种情况,接下来看就知道了。还有为什么在这里才实例化SpinnerDropDownPopupWindow对象,是因为在实例SpinnerDropDownPopupWindow对象时需要用的一个MySpinner按钮的宽度,如果提前实例化的话,这时候控件还没有在屏幕上显示,MySpinner按钮是没有宽高值的,所以放在按钮的点击监听事件中,这时候已经在屏幕上全部显示出来,可以测量出来按钮的宽度了。

后面的就是定义SpinnerDropDownPopupWindow的过程,细节就不介绍了,只强调几点:

(1):setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
      setFocusable(true);//得到焦点
      setOutsideTouchable(true);//点击布局外失去焦点

这三行代码就是在点击PopupWindow的外面时,让其失去焦点从而消失(这就是为什么没有为MySpinner绑定另一种情况,一是你点不到那个按钮,二是没必要绑定)

(2):需要为PopupWindow中的listview对象绑定一个OnItemClickListener监听器,在这个监听器中,一是要修改MySpinner按钮上的文字,二是要回调activity中的OnItemSelectedListener监听器(就是这里用的MySpinner中的OnItemSelectedListener对象)。这样基本完美模拟了spinner的整个过程


MySpinner中用到的两个布局文件

my_spinner.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/dialog_bottom_holo_light"
    android:cacheColorHint="#00000000"
    android:orientation="vertical" >
    <ListView
        android:id="@+id/my_spinner_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:cacheColorHint="#00000000"
        android:focusableInTouchMode="true"
        android:focusable="true"
        android:listSelector="@drawable/my_spinner_list_set"
        android:divider="@color/spinner_list_divider"
        android:dividerHeight="1px"
        android:background="@color/spinner_list_background" >
    </ListView>

</LinearLayout>

my_spinner_item.xml

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

    <TextView
        android:id="@+id/my_spinner_item_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center_vertical"
        android:paddingLeft="6dp"
        android:paddingRight="6dp"
        android:paddingTop="11dp"
        android:paddingBottom="12dp"
        android:textSize="16sp"
        android:textColor="@color/black"
        android:singleLine="true" />

</LinearLayout>


这里面的padding值和字体大小都是我实测出来了,眼睛都花了,累死.....TT


最后上用到的Activity的代码和布局

Test.java

package com.example.myspinner;

import java.util.ArrayList;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;

public class Test extends Activity{

	private MySpinner spinner = null;
	private ArrayList<String> data = null;
	
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.test);
		
		spinner = (MySpinner)findViewById(R.id.spinner_btn);
		data = new ArrayList<String>();
		String[] item = { "   -", "大学", "中学", "小学", "企业", "地区", "商业" };
		for( int i = 0; i < 7; i++ ){
			data.add(item[i]);
		}
		spinner.setData(data);
		spinner.setOnItemSelectedListener(new SpinnerOnItemSelectedListener());
	}
	
	class SpinnerOnItemSelectedListener implements OnItemSelectedListener{

		public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
			System.out.println("Data--->" + data.get(arg2));
		}

		public void onNothingSelected(AdapterView<?> arg0) {
			
		}
		
	}

	
}

test.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:gravity="top|center_horizontal"
	android:paddingTop="10dp"
 	android:orientation="vertical" >
	<com.example.myspinner.MySpinner
		android:id="@+id/spinner_btn"
		android:layout_width="200dp"
		android:layout_height="wrap_content"
		android:background="@drawable/spinner"
		android:text="请选择分类"
		android:textSize="16sp"
		android:paddingLeft="10dp"
		android:paddingRight="10dp"
		android:paddingTop="7dp"
		android:paddingBottom="9dp"
		android:gravity="center_vertical|left" />

</LinearLayout>

这里基本就是全部的过程了。

但是假如你以为到了这里就完了,错了。如果你对spinner的下拉列表的XML文件(就是my_spinner.xml)用的背景是spinner_dropdown_background_down.9.png,哈哈,你就上当了,这个就不是4.0中用的背景。当时我也是比较诧异的,后来我无意中发现一个文件dialog_bottom_holo_light.9.png有几分神似(这个文件和其他贴图文件一样都在\res的那个目录中),当我添加进测试完后发现,谷歌,可真调皮...........→_→

(最后,我发现了一个bug,在android4.2的机器上测试是没问题的,后来我在2.3的机器上出现了一点小问题。就是当你在下拉列表中选中一项时,要修改选中项的背景色时,最后整个列表背景色全被修改了,最后经过查找找到了罪魁祸首,原来我配置的listview的listSelector属性中的select文件中,设置的是当获得焦点是,利用一个颜色修改背景色,现在改成用一张9.png图片铺满时就没有这个问题。我已经重新上传了新的工程)

好了,有疑问的朋友请在下面留言。


工程下载点击这里


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值