android自帶的下拉框好用不?我覺得有時候好用,有時候難有,項目規定這樣的效果,自帶的控件實作不了,那麼隻有我們自己來老老實實滴寫一個新的了,其實最基本的下拉框就像一些資料填寫時,點選的時候出現在編輯框的下面,然後又很多選項的下拉框,可是我在網上找了一下,沒有這種下拉框額,就自己寫了一個,看效果圖先:

,這個是資料填寫的一部分界面,三個下拉框,選擇故鄉所在地;
點選之後彈出下拉框,選擇下面的選項;
三個下拉框時關聯的,第一個決定了第二資料内容,第二個決定了第三個資料内容,如果三個全部選好之後,再次點選第一個,那麼第二個、第三個都會清空,點選第二個則第三個會清空。
要實作它,也就是一個PopupWindow時主要的界面,下面來看看代碼:
建立一個DefineSpinnerView.java檔案,繼承至View,然後給出如下屬性:
/**
* 用于彈出的下拉框
*/
private PopupWindow pWindow = null;
// **************************************************************************
// 這些是用來當點選一個時,根據他們之間的關系來顯示下拉框中的内容
// **************************************************************************
/**
* 祖父
*/
private DefineSpinnerView gradeParent = null;
/**
* 父控件
*/
private DefineSpinnerView parents = null;
/**
* 子控件
*/
private DefineSpinnerView child1 = null;
/**
* 孫子控件
*/
private DefineSpinnerView child2 = null;
private Context context = null;
private OptionsAdapter adapter = null; // 下拉框擴充卡
private List<String> datas = null; // 下拉框資料
private RelativeLayout layout = null; // 父控件
private TextView text = null; // 文本顯示
private ImageView image = null; // 下拉箭頭
private int p_width = -1; // 下拉框寬度
private ListView list = null; // 下拉表
在構造函數中,構造出一個TextView和一個ImageView控件,并将它們都添加到layout中,代碼如下:
TextListener lis = new TextListener();
text = new TextView(context);
text.setBackgroundResource(R.drawable.edit_normal);
text.setTextColor(getResources().getColor(R.color.spinner_text));
text.setGravity(Gravity.CENTER);
text.setOnClickListener(lis);
LayoutParams params1 = new LayoutParams(width, hight);
params1.leftMargin = left;
params1.topMargin = top;
image = new ImageView(context);
image.setBackgroundResource(R.drawable.gerendang_jiantou);
image.setOnClickListener(lis);
if (LoginAct.MACHINE_PIXELS == IFinalConstant.XHDPI_RESOLUTION) {
text.setTextSize(20.0f);
LayoutParams params2 = new LayoutParams(19, 17);
params2.topMargin = top + 15;
params2.leftMargin = left + width - 28;
map.put(image, params2);
} else {
text.setTextSize(15.0f);
LayoutParams params2 = new LayoutParams(8, 8);
params2.topMargin = top + 13;
params2.leftMargin = left + width - 16;
map.put(image, params2);
}
map.put(text, params1);
裡面涉及到一個TextListener内部類,是我們自己定義的一個類,它繼承至OnClickListener接口
/**
* @author ZYJ
* 當點選Text時,根據上一級的内容來設定下一級的内容
*/
class TextListener implements OnClickListener {
public void onClick(View v) {
hideSoft ();
if (gradeParent != null && parents != null) {
DefineSpinnerView.this.setDatas(DefineSpinnerView.this
.getGuxiang3(gradeParent.getText(), parents.getText()));
}
if (gradeParent == null && parents != null) {
DefineSpinnerView.this.setDatas(DefineSpinnerView.this
.getGuxiang2(parents.getText()));
}
cleanText();
changPopState(text);
}
這個裡面調用了一個方法changPopState,它的定義如下:
/**
* 顯示或者隐藏下拉框
*
* @param v
*/
private void changPopState(View v) {
if (pWindow == null) {
popWindow(v);
return;
}
if (!pWindow.isShowing()) {
popWindow(v);
} else {
if (pWindow != null) {
pWindow.dismiss();
}
}
}
這個裡面又調用了一個popWindow方法,定義如下:
/**
* 初始化下拉框
*
* @param par 父控件
*/
private void popWindow(final View par) {
if (pWindow == null) {
// 布局檔案
View v = LayoutInflater.from(context).inflate(R.layout.list, null);
list = (ListView) v.findViewById(R.id.list);
list.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
// R.String.butian代表的是“不填”
if (datas.get(arg2).toString().equals(context.getString(R.string.butian))) {
text.setText("");
} else {
text.setText(datas.get(arg2).toString()); // 将目前點選的item中的字元串顯示出來
}
if (pWindow != null) { // 關閉下拉框
changPopState(par);
}
}
});
adapter = new OptionsAdapter(context, datas); // 根據資料,設定下拉框顯示
list.setAdapter(adapter);
list.setDivider(null); // 屏蔽下拉框每個item之間的線條
/**
* 兩種不同長度的下拉框,主要是為了适應螢幕的大小
*/
if (p_width > 0) {
pWindow = new PopupWindow(v, par.getWidth(), 150);
} else {
pWindow = new PopupWindow(v, par.getWidth(), 300);
}
pWindow.setFocusable(true);
pWindow.setBackgroundDrawable(new BitmapDrawable());
pWindow.setOutsideTouchable(true);
pWindow.update();
}
pWindow.showAsDropDown(text);
}
然後是一些細節了,提供一個TextView設定上面文字和得到上面文字的方法,設定下拉框資料的方法setDatas,如下:
public void setText(String str) {
if (text != null) {
text.setText(str);
}
}
public void setDatas(List<String> datas) {
this.datas = datas;
if (adapter != null) {
adapter.setDatas(datas);
adapter.notifyDataSetInvalidated();
}
}
public String getText() {
if (text != null) {
return text.getText().toString();
}
LoginAct.LogW("spinner's textView is null");
return "";
}
private void cleanText() {
if (child1 != null) {
child1.text.setText("");
}
if (child2 != null) {
child2.text.setText("");
}
}
然後添加幾個關聯控件的get方法:
public void setChild1(DefineSpinnerView child1) {
this.child1 = child1;
}
public void setChild2(DefineSpinnerView child2) {
this.child2 = child2;
}
public void setGradeParent(DefineSpinnerView gradeParent) {
this.gradeParent = gradeParent;
}
public void setParents(DefineSpinnerView parents) {
this.parents = parents;
}
public void setP_width(int p_width) {
this.p_width = p_width;
}
接下來提供一個設定子控件和算子控件資料的方法:
/**
* @param s1 父控件中的字元串
* @param s2 子控件中的字元串
* @return 傳回一個List<String>集合
* @功能 通過父控件的字元串來設定子控件中的内容
*/
private List<String> getGuxiang3(String s1, String s2) {
List<String> dd = new ArrayList<String>();
dd.add(context.getString(R.string.butian));
Map<String, ArrayList<String>> mapTmp1 = MaterialView.cityMap.get(s1);
if (mapTmp1 != null) {
List<String> list = mapTmp1.get(s2);
if (list != null) {
for (String str : list) {
dd.add(str);
}
}
}
return dd;
}
/**
* @param s 字元串
* @return
* @author ZYJ
* @功能 設定父親輩的下拉框中的内容
*/
private List<String> getGuxiang2(String s) {
List<String> dd = new ArrayList<String>();
dd.add(context.getString(R.string.butian));
Map<String, ArrayList<String>> mapTmp = MaterialView.cityMap.get(s);
if (mapTmp != null) {
for (String str : mapTmp.keySet()) {
dd.add(str);
}
}
return dd;
}
最後提供一個隐藏軟鍵盤的方法:
private void hideSoft() {
InputMethodManager imm = (InputMethodManager) context
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT,
InputMethodManager.HIDE_NOT_ALWAYS);
}
到這裡,自定義控件的代碼基本上寫完了;我們還要來看看下拉框中的xml布局和擴充卡的寫法:
xml檔案:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/info"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:textSize="15sp"
android:textColor="@color/spinner_text"
android:layout_gravity="center"
android:gravity="center"/>
</LinearLayout>
然後是擴充卡類(OptionsAdapter),看全部代碼吧:
public class OptionsAdapter extends BaseAdapter {
private Context context = null;
private List<String> datas = null;
public OptionsAdapter(Context context, List<String> d) {
this.context = context;
this.datas = d;
}
public int getCount() {
return datas.size();
}
public Object getItem(int arg0) {
return datas.get(arg0);
}
public long getItemId(int arg0) {
return arg0;
}
/**
* @author ZYJ
* @功能 一個簡單TextView顯示
*/
public View getView(int arg0, View arg1, ViewGroup arg2) {
View view = LayoutInflater.from(context).inflate(R.layout.childlist,
null);
TextView textStr = (TextView) view.findViewById(R.id.info);
textStr.setText("\t" + getItem(arg0).toString());
return view;
}
public void setDatas(List<String> datas) {
this.datas = datas;
}
}
這樣,上面的功能基本上都實作了,我的這個控件在我項目中是手動添加上去的,而不是定義在xml檔案中的,是以也不知道定義在xml檔案中能不能生效,各位盡管試試吧。