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
这是MySpinner按钮的背景
spinner.xml
这是下拉列表的单个选项的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 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();
setOnClickListener( new SpinnerButtonOnClickListener() );
}
/**
* 设置spinner的监听器,用于回调,务必在显示下拉列表前载入
* @param listener
*/
public void setOnItemSelectedListener( OnItemSelectedListener listener ){
this.listener = listener;
}
/**
* 这个方法在显示前调用,装载入数据
* @param data
*/
public void setData( ArrayList 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
接下来,是定义一个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
这里面的padding值和字体大小都是我实测出来了,眼睛都花了,累死.....TT
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 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[] 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) {
}
}
}
但是假如你以为到了这里就完了,错了。如果你对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图片铺满时就没有这个问题。我已经重新上传了新的工程)
好了,有疑问的朋友请在下面留言。
工程下载点击这里