选择对话框:自定义组合控件+自定义对话框 实现

由于项目的需要,需要用到选择对话框,虽然可以使用系统自带到控件就可以实现,但是从长远的角度上去看,还是不利于本项目的发展,于是自己做了一个自定义的控件,使用到了组合控件,顺便也学习了下组合控件的创建。

自定义控件有多种方式,具体就不提及了,本次俺只使用组合控件,先上个图,让大家对本次所实现的效果有个直观的认识:


如上图所示,我希望能在点击“浏览模式”的时候弹出选择对话框(注意:这个按钮有标题和当前选择项,右边还有一个图标以提示用户),而且这个对话框是可以使用自定义的对话框。针对这种实现,我想到的可能的几种方法:

  1. 使用ListPreference
    • 难点1:使用PreferenceActivity的背景颜色不知道可以选择不? 个人觉得应该可以更改掉
    • 难点2:Preference不是要保存数据么?如果不保存数据要如何去做? 经过查证,可以设置它的persistence属性,使其不保存数据
    • 难点3:能够更改对话框的布局?应该可以,俺不确定,有待求证
  2. 使用Spinner
    • 难点1:主页面(标题、当前选项、右边加一个图标)显示使用组合对话框应该可以解决,不过好像也蛮麻烦的
    • 难点2:能够更改对话框的布局?应该可以,俺不确定,有待求证
  3. 完全自定义
    • 难点1:控件的美观程度?应该可以用图片解决
    • 难点2:弹出对话框后选择选项后改变相应的内容
    • 难点3:自定义对话框布局

起初做的时候,可能查证的信息不够多,因此,头两种方式没有求尝试实现,当时就选择了最后的一种方式。


我们选择了自定义控件的方式,于是定义了一个类ListSelectView extends LinearLayout,有如下变量

	private TextView tvHeader;    //Item 的标题
	private TextView tvContent;   //Item的选项
	
	private CharSequence[] mEntries;   //对话框的选项
        private CharSequence[] mEntryValues;  //选择对话框后的值
	private int mClickedDialogEntryIndex; //对话框选项的index


1、item项目实现

首先是主页的item项:



定义了如下布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="horizontal"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"  android:paddingLeft="10dp" android:paddingRight="10dp" android:paddingTop="10dp" android:paddingBottom="4dp">
    
    <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" android:layout_weight="1">
    	<TextView android:id="@+id/tvListSelectLayoutTitle" android:layout_width="fill_parent" android:layout_height="wrap_content" android:paddingBottom="0dp" />
    	<TextView android:id="@+id/tvListSelectLayoutContent" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="10dp" android:paddingTop="0dp" />
    </LinearLayout>
    <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:scaleType="fitCenter" android:background="@drawable/ic_btn_round_more_normal"/>
</LinearLayout>


就大致了实现了这样的布局,然后在attrs.xml中定义了两个属性(可根据情况添加)

    <declare-styleable name="ListSelectView">
        <attr name="entries" format="reference"/>
        <attr name="entryValues" format="reference" />
    </declare-styleable>

之后在在ListSelectView构造函数中初始化控件及状态

	public ListSelectView(Context context, AttributeSet attrs) {
		super(context, attrs);
		LayoutInflater.from(context).inflate(R.layout.list_select_layout, this, true);
		TypedArray a = context.obtainStyledAttributes(attrs, org.lansir.R.styleable.ListSelectView, 0, 0);
		mEntries = a.getTextArray(org.lansir.R.styleable.ListSelectView_entries);  //从属性处初始化值
		mEntryValues = a.getTextArray(org.lansir.R.styleable.ListSelectView_entryValues);
		a.recycle();

		tvHeader = (TextView) findViewById(R.id.tvListSelectLayoutTitle);  //初始化控件
		tvContent = (TextView) findViewById(R.id.tvListSelectLayoutContent);
		tvHeader.setTextColor(Color.BLACK);  //设置标题颜色
		tvContent.setGravity(Gravity.TOP);
		this.setOnClickListener(this); //设置点击事件
	}



得到的效果如上,具体的美工在后期可通过贴图实现

这样,Item项就初始化完毕了

2、对话框实现

接下来是对话框的视图:

上节中,我们设置了点击事件,事件的内容如下:

	@Override
	public final void onClick(View v) {

		SingleSelectionDialog dialog = new SingleSelectionDialog.Builder(this.getContext()).setTitle(tvHeader.getText()).setSingleChoiceItems(mEntries,
				mClickedDialogEntryIndex, new DialogInterface.OnClickListener() {

					@Override
					public void onClick(DialogInterface dialog, int which) {
						mClickedDialogEntryIndex = which;
						tvContent.setText(mEntryValues[mClickedDialogEntryIndex]);
						dialog.dismiss();
					}

				}).create();

		dialog.show();


	}

在这里自定义了一个对话框,SingleSelectionDialog是一个继承自Dialog的一个类,之所以不继承自AlertDialog,是因为AlertDialog要去自定义内容及Style比Dialog类还麻烦,要通过比较复杂的方式去实现。同时,这里setSingleChoiceItems是SingleSelectionDialog类自定义的一个封装方法,这里设置了对话框List选项的值及事件,在这个事件里,设置选项的Index,以便下次再次点击对话框时设置成已设置的值,同时,这里改变主页Item选项值t的内容及关闭对话框。


SingleSelectionDialog的代码实现如下:

public class SingleSelectionDialog extends Dialog {

	public SingleSelectionDialog(Context context, boolean cancelable,
			OnCancelListener cancelListener) {
		super(context, cancelable, cancelListener);

	}

	public SingleSelectionDialog(Context context, int theme) {
		super(context, theme);
	}

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

	public static class Builder {

		private Context context;
		private CharSequence title;  //对话框标题
		private CharSequence[] mListItem; //对话框选项值
		private int mClickedDialogEntryIndex;  //对话框选项Index
		private DialogInterface.OnClickListener mOnClickListener;  //对话框点击事件

		public Builder(Context context) {
			this.context = context;
		}

		public Builder setTitle(int title) {
			this.title = (String) context.getText(title);
			return this;
		}

		public Builder setTitle(CharSequence title) {
			this.title = title;
			return this;
		}

		public CharSequence[] getItems() {
			return mListItem;
		}

		public Builder setItems(CharSequence[] mListItem) {
			this.mListItem = mListItem;
			return this;
		}
		
		//设置单选List选项及事件,这些属性在之后的create中用到,这里使用Android系统创建dialog的风格
		public Builder setSingleChoiceItems(CharSequence[] items, int checkedItem, final OnClickListener listener) {
			this.mListItem = items;
                        this.mOnClickListener = listener;
                        this.mClickedDialogEntryIndex = checkedItem;
                        return this;
                } 

	
		public SingleSelectionDialog create() {
			LayoutInflater inflater = (LayoutInflater) context
					.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			final SingleSelectionDialog dialog = new SingleSelectionDialog(
					context, R.style.Theme_Dialog_ListSelect);
			View layout = inflater.inflate(R.layout.single_selection_dialog,
					null);
			dialog.addContentView(layout, new LayoutParams(
					LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
			
			if(mListItem == null){
				throw new RuntimeException("Entries should not be empty");
			}
			ListView lvListItem = (ListView) layout.findViewById(R.id.lvListItem);
//			android.R.layout.simple_list_item_single_choice
			//lvListItem.setAdapter(new ArrayAdapter(context, android.R.layout.simple_list_item_single_choice, android.R.id.text1, mListItem));
//			SingleSelectionAdapter mSingleSelectionAdapter = new SingleSelectionAdapter(context, R.layout.single_list_item, R.id.ctvListItem, mListItem);
//			lvListItem.setAdapter(mSingleSelectionAdapter);
			lvListItem.setAdapter(new ArrayAdapter(context,  R.layout.single_selection_list_item, R.id.ctvListItem, mListItem));
			lvListItem.setOnItemClickListener(new OnItemClickListener(){

				@Override
				public void onItemClick(AdapterView<?> parent, View view,
						int position, long id) {
					mOnClickListener.onClick(dialog, position);

				}
				
			});
			lvListItem.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
			lvListItem.setItemChecked(mClickedDialogEntryIndex, true);
			lvListItem.setSelection(mClickedDialogEntryIndex);
		

			TextView tvHeader = (TextView)layout.findViewById(R.id.title);
			tvHeader.setText(title);
					
			
			return dialog;
		}		
		
	}

}


setSingleSelectItems设置了单选列表的Item以及事件,这些属性在create中用到。

最关键的就是create()方法,在这里初始化dialog的主题:

  • 取消标题
  • 取消边框
  • 使用透明
style样式如下:

	<style name="Theme.Dialog.ListSelect" parent="android:style/Theme.Dialog">
		<item name="android:windowIsFloating">true</item>
		<item name="android:windowIsTranslucent">false</item><!--半透明-->
		<item name="android:backgroundDimEnabled">false</item><!--模糊-->
		<item name="android:windowContentOverlay">@null</item>
		<item name="android:windowNoTitle">true</item> 
		<item name="android:windowFrame">@null</item><!--边框-->
	</style>


之后是使用setContent初始化内容,这里,使用自定义布局的方式去创建,xml布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical" android:layout_width="fill_parent"
	android:minWidth="280dip" android:layout_height="wrap_content">
         //标题
	<LinearLayout android:orientation="vertical"
		android:background="@drawable/header" android:layout_width="fill_parent"
		android:layout_height="wrap_content">

		<TextView style="@style/DialogText.Title" android:id="@+id/title"
			android:paddingRight="8dip" android:paddingLeft="8dip"
			android:background="@drawable/title" android:layout_width="wrap_content"
			android:layout_height="wrap_content" android:textColor="@color/black" />
	</LinearLayout>
        //内容
	<LinearLayout android:id="@+id/content"
		android:orientation="vertical" android:background="@drawable/center"
		android:layout_width="fill_parent" android:layout_height="wrap_content">
		<ListView android:layout_width="match_parent"
			android:layout_height="match_parent" android:layout_marginTop="5px"
			android:cacheColorHint="@null" android:divider="@android:drawable/divider_horizontal_bright"
			android:scrollbars="vertical" android:id="@+id/lvListItem" />

	</LinearLayout>//底部
	<LinearLayout android:orientation="horizontal"
		android:background="@drawable/footer" android:layout_width="fill_parent"
		android:layout_height="wrap_content" />

</LinearLayout>


整个布局分为:头部、内容、底部。头部用于显示标题, 内容用于显示单选List, 底部用于美化对话框用。

然后设置Adapter,使得adapter的layout使用我们自定义的layout,这个layout设置list选项的样式:

<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ctvListItem"
    android:layout_width="match_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:textAppearance="?android:attr/textAppearanceLarge"
    android:gravity="center_vertical"
    android:checkMark="@drawable/radio_button_selector"
    android:paddingLeft="6dip"
    android:paddingRight="6dip"
    android:textColor="@color/black"

/>

在做这个layout的时候,当初确实不知道如何去做,自己本想定义一个TextView 以及一个RadioButton解决,后来发现这种解决方式一直不理想,主要问题在如何设置单选按钮,实现它的单选功能?用我们自定义的Adapter必须检测是哪个item被选中,但由于ListView的缓存机制增加了这种做法的复杂程度,我们还得为之做个缓冲,保存单选的状态才能实现。而且查看了系统Dialog的源码,发现其本就使用了缓存选项状态的方式去实现,那我们干嘛要自己定义,自己实现,还那么复杂,为何不去使用它呢?于是便查看参考了android.R.layout.simple_list_item_single_choice布局文件的内容,发现它用到了CheckedTextView的控件,但是我们并不想要系统默认的单选样式。于是,自己自定义了一个CheckedTextView,改掉它的单选样式(即android:checkMark)。

于是创建布局完毕,开始最后一步整合,即设置单选列表,容易出问题的地方来了:

			ListView lvListItem = (ListView) layout.findViewById(R.id.lvListItem);
			lvListItem.setAdapter(new ArrayAdapter(context,  R.layout.single_selection_list_item, R.id.ctvListItem, mListItem));
			lvListItem.setOnItemClickListener(new OnItemClickListener(){

				@Override
				public void onItemClick(AdapterView<?> parent, View view,
						int position, long id) {
					mOnClickListener.onClick(dialog, position);

				}
				
			});
			lvListItem.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
			lvListItem.setItemChecked(mClickedDialogEntryIndex, true);
			lvListItem.setSelection(mClickedDialogEntryIndex);
我们使用系统自定义的Adapter,发现它能为我们解决很多问题,如上述的缓存选项状态的问题。这里表明看起来没什么讲究,其实大有讲究:setChoiceMode必须在setItemChecked前调用,否则setChoiceMode会失效,这个也是我查看了Android的ListView的源码实现后才发现的。

这样,选择对话框:自定义组合控件+自定义对话框 完成,使用起来超级简单,完全不需要自己去做很多的设置,我的整个Activity如:

public class SharePrefersActivity extends Activity {
    
	private ListSelectView mListSelectView;
	
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mListSelectView = (ListSelectView)findViewById(R.id.lsvTest);
        mListSelectView.setHeader("Hello");
        mListSelectView.setContent("world");

    }

便能实现如下的效果,来个截图纪念下:



这样,就大功告成了。

3、注意选项

在做这个DEMO的时候,我想设置主页Item的select,于是便在构造函数里使用this.setBackground()去设置,发现这样设置后,在xml里对ListSelectView设置paddingLeft, layout_margin等属性时都会便无效,这里记住这个错误的方式,以提醒后来人!


源代码下载

版权所有,转载请出明出处!



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值