您的位置:首页 > 其它

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

2014-11-12 23:57 363 查看
转:http://blog.csdn.net/billpig/article/details/6664053 

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

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



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

使用ListPreference
难点1:使用PreferenceActivity的背景颜色不知道可以选择不? 个人觉得应该可以更改掉
难点2:Preference不是要保存数据么?如果不保存数据要如何去做?  经过查证,可以设置它的persistence属性,使其不保存数据
难点3:能够更改对话框的布局?应该可以,俺不确定,有待求证

使用Spinner
难点1:主页面(标题、当前选项、右边加一个图标)显示使用组合对话框应该可以解决,不过好像也蛮麻烦的
难点2:能够更改对话框的布局?应该可以,俺不确定,有待求证

完全自定义
难点1:控件的美观程度?应该可以用图片解决
难点2:弹出对话框后选择选项后改变相应的内容
难点3:自定义对话框布局

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

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

[java] view
plaincopy

private TextView tvHeader;    //Item 的标题  

private TextView tvContent;   //Item的选项  

  

private CharSequence[] mEntries;   //对话框的选项  

       private CharSequence[] mEntryValues;  //选择对话框后的值  

private int mClickedDialogEntryIndex; //对话框选项的index  

1、item项目实现

首先是主页的item项:



定义了如下布局:

[html] view
plaincopy

<?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中定义了两个属性(可根据情况添加)

[html] view
plaincopy

<declare-styleable name="ListSelectView">  

    <attr name="entries" format="reference"/>  

    <attr name="entryValues" format="reference" />  

</declare-styleable>  

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

[java] view
plaincopy

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、对话框实现

接下来是对话框的视图:



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

[java] view
plaincopy

@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的代码实现如下:

[java] view
plaincopy

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样式如下:

[java] view
plaincopy

<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布局文件如下:

[java] view
plaincopy

<?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选项的样式:

[java] view
plaincopy

<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)。

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

[java] view
plaincopy

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如:

[java] view
plaincopy

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等属性时都会便无效,这里记住这个错误的方式,以提醒后来人!

源代码下载

版权所有,转载请出明出处!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐