一個封裝好的Android仿Ios ActionSheet控件

這次還是帶來一個Android仿Ios風格的控件。Ios裏有個控件叫ActionSheet,就是那種從下往上彈出的選項卡,比如下面這個效果:

                                                                 

UI在設計效果圖的時候,添加圖片、選擇性別之類的功能經常會這麼設計,ios那邊有個ActionSheet可以用,android這邊就比較麻煩了(有時候會感嘆ios的一些控件確實要比android封裝得更徹底)於是就自己封裝了一個,名字也叫ActionSheet。控件其實是很久以前寫的,現在拿出來跟大家分享一下。話不多說,正片開始。


首先這個控件繼承自Dialog,需要先定義一個style和彈入彈出的動畫。首先在res目錄下新建一個anim文件夾,並在這個文件夾下新建兩個動畫文件

actionsheet_in.xml和 actionsheet_out,xml,如下:

actionsheet_in.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="200"
    android:fromYDelta="100%"
    android:toYDelta="0" />

actionsheet_out.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="200"
    android:fromYDelta="0"
    android:toYDelta="100%" />

然後就定義一個style並綁定這兩個動畫。在styles文件先添加以下內容:

<style name="ActionSheetStyle" parent="@android:style/Theme.Dialog">
    <!-- 背景透明 -->
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:windowContentOverlay">@null</item>
    <!-- 浮於Activity之上 -->
    <item name="android:windowIsFloating">true</item>
    <!-- 邊框 -->
    <item name="android:windowFrame">@null</item>
    <!-- Dialog以外的區域模糊效果 -->
    <item name="android:backgroundDimEnabled">true</item>
    <!-- 無標題 -->
    <item name="android:windowNoTitle">true</item>
    <!-- 半透明 -->
    <item name="android:windowIsTranslucent">true</item>
    <!-- Dialog進入及退出動畫 -->
    <item name="android:windowAnimationStyle">@style/ActionSheetAnimation</item>
</style>


<!-- ActionSheet進出動畫 -->
<style name="ActionSheetAnimation" parent="@android:style/Animation.Dialog">
    <item name="android:windowEnterAnimation">@anim/actionsheet_in</item>
    <item name="android:windowExitAnimation">@anim/actionsheet_out</item>
</style>

然後是最麻煩的地方,定義背景資源文件,一共12個(沒辦法,好看是有代價的 >_<)。在drawable目錄下創建以下12個資源文件:

dialog_bottom_up.xml:

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

    <solid android:color="#e3ffffff" />

    <corners
        android:bottomLeftRadius="10dp"
        android:bottomRightRadius="10dp" />

</shape>
dialog_bottom_down.xml:

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

    <solid android:color="#e3eeeeee" />

    <corners
        android:bottomLeftRadius="10dp"
        android:bottomRightRadius="10dp" />

</shape>

dialog_bottom_selector.xml:

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

    <item android:drawable="@drawable/dialog_bottom_up" android:state_pressed="false"></item>
    <item android:drawable="@drawable/dialog_bottom_down" android:state_pressed="true"></item>

</selector>

dialog_top_up.xml:

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

    <solid android:color="#e3ffffff" />

    <corners
        android:topLeftRadius="10dp"
        android:topRightRadius="10dp" />

</shape>

dialog_top_down.xml:

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

    <solid android:color="#e3eeeeee" />

    <corners
        android:topLeftRadius="10dp"
        android:topRightRadius="10dp" />

</shape>
dialog_top_selector.xml:

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

    <item android:drawable="@drawable/dialog_top_up" android:state_pressed="false"></item>
    <item android:drawable="@drawable/dialog_top_down" android:state_pressed="true"></item>

</selector>

dialog_white_up.xml:

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

    <solid android:color="#e3ffffff" />

    <corners android:radius="10dp" />

</shape>

dialog_white_down.xml:

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

    <solid android:color="#e3eeeeee" />
    
</shape>

dialog_white_selector.xml:

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

    <item android:drawable="@drawable/dialog_white_up" android:state_pressed="false"></item>
    <item android:drawable="@drawable/dialog_white_down" android:state_pressed="true"></item>

</selector>

layout_white_up.xml:

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

    <solid android:color="#e3ffffff" />
    
</shape>

layout_white_down.xml:

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

    <solid android:color="#e3eeeeee" />
    
</shape>

layout_white_selector.xml:

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

    <item android:drawable="@drawable/layout_white_up" android:state_pressed="false"></item>
    <item android:drawable="@drawable/layout_white_down" android:state_pressed="true"></item>

</selector>

終於搞好了,好麻煩的說。接下來就可以上ActionDialog的本體了,如下:

ActionDialog.java:

public class ActionSheet extends Dialog {
    private Context context;

    private LinearLayout parentLayout;
    private TextView titleTextView;
    private ArrayList<Button> sheetList;
    private Button cancelButton;

    // 標題
    private String title;

    //就取消按鈕文字
    private String cancel;

    // 選擇項文字列表
    private ArrayList<String> sheetTextList;

    // 標題顏色
    private int titleTextColor;

    // 取消按鈕文字顏色
    private int cancelTextColor;

    // 選擇項文字顏色
    private int sheetTextColor;

    // 標題大小
    private int titleTextSize;

    // 取消按鈕文字大小
    private int cancelTextSize;

    // 選擇項文字大小
    private int sheetTextSize;

    // 標題欄高度
    private int titleHeight;

    // 取消按鈕高度
    private int cancelHeight;

    // 選擇項高度
    private int sheetHeight;

    // 彈出框距離底部的高度
    private int marginBottom;

    // 取消按鈕點擊回調
    private View.OnClickListener cancelListener;

    // 選擇項點擊回調列表
    private ArrayList<View.OnClickListener> sheetListenerList;

    public ActionSheet(Context context) {
        super(context, R.style.ActionSheetStyle);
        init(context);
    }

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

    private void init(Context context) {
        this.context = context;

        cancel = "取消";
        titleTextColor = Color.parseColor("#aaaaaa");
        cancelTextColor = Color.parseColor("#666666");
        sheetTextColor = Color.parseColor("#666666");
        titleTextSize = 14;
        cancelTextSize = 16;
        sheetTextSize = 16;
        titleHeight = dp2px(40);
        cancelHeight = dp2px(40);
        sheetHeight = dp2px(40);
        marginBottom = dp2px(10);

        sheetList = new ArrayList<>();
        sheetTextList = new ArrayList<>();
        sheetListenerList = new ArrayList<>();
    }

    private ActionSheet createDialog() {
        parentLayout = new LinearLayout(context);
        parentLayout.setBackgroundColor(Color.parseColor("#00000000"));
        parentLayout.setOrientation(LinearLayout.VERTICAL);

        if (title != null) {
            titleTextView = new TextView(context);
            titleTextView.setGravity(Gravity.CENTER);
            titleTextView.setText(title);
            titleTextView.setTextColor(titleTextColor);
            titleTextView.setTextSize(titleTextSize);
            titleTextView.setBackgroundDrawable(context.getResources().getDrawable(R.drawable.dialog_top_up));
            LinearLayout.LayoutParams titleLayoutParams = new LinearLayout.LayoutParams
                    (ViewGroup.LayoutParams.MATCH_PARENT, titleHeight);
            parentLayout.addView(titleTextView, titleLayoutParams);
        }
        for (int i = 0; i < sheetTextList.size(); i++) {
            if (i == 0 && title != null) {
                View topDividerLine = new View(context);
                topDividerLine.setBackgroundColor(Color.parseColor("#eeeeee"));
                parentLayout.addView(topDividerLine, new LinearLayout
                        .LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp2px(1)));
            }

            Button button = new Button(context);
            button.setGravity(Gravity.CENTER);
            button.setText(sheetTextList.get(i));
            button.setTextColor(sheetTextColor);
            button.setTextSize(sheetTextSize);
            if (title != null) {
                if (i == sheetTextList.size() - 1) {
                    button.setBackgroundDrawable(context.getResources().getDrawable(R.drawable.dialog_bottom_selector));
                } else {
                    button.setBackgroundDrawable(context.getResources().getDrawable(R.drawable.layout_white_selector));
                }
            } else {
                if (sheetTextList.size() == 1) {
                    button.setBackgroundDrawable(context.getResources().getDrawable(R.drawable.dialog_white_selector));
                } else {
                    if (i == 0) {
                        button.setBackgroundDrawable(context.getResources().getDrawable(R.drawable.dialog_top_selector));
                    } else if (i == sheetTextList.size() - 1) {
                        button.setBackgroundDrawable(context.getResources().getDrawable(R.drawable.dialog_bottom_selector));
                    } else {
                        button.setBackgroundDrawable(context.getResources().getDrawable(R.drawable.layout_white_selector));
                    }
                }
            }
            button.setOnClickListener(sheetListenerList.get(i));
            LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams
                    (ViewGroup.LayoutParams.MATCH_PARENT, sheetHeight);
            parentLayout.addView(button, layoutParams);
            sheetList.add(button);

            if (i != sheetTextList.size() - 1) {
                View bottomDividerLine = new View(context);
                bottomDividerLine.setBackgroundColor(Color.parseColor("#eeeeee"));
                parentLayout.addView(bottomDividerLine, new LinearLayout
                        .LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp2px(1)));
            }
        }

        cancelButton = new Button(context);
        cancelButton.setGravity(Gravity.CENTER);
        cancelButton.setText(cancel);
        cancelButton.setTextColor(cancelTextColor);
        cancelButton.setTextSize(cancelTextSize);
        cancelButton.setBackgroundDrawable(context.getResources().getDrawable(R.drawable.dialog_white_selector));
        cancelButton.setOnClickListener(cancelListener);
        LinearLayout.LayoutParams cancelParams = new LinearLayout.LayoutParams
                (ViewGroup.LayoutParams.MATCH_PARENT, cancelHeight);
        cancelParams.setMargins(0, dp2px(10), 0, 0);
        parentLayout.addView(cancelButton, cancelParams);

        getWindow().setGravity(Gravity.BOTTOM);
        getWindow().getAttributes().y = marginBottom;
        show();
        setContentView(parentLayout);
        setCancelable(true);
        setCanceledOnTouchOutside(true);

        return this;
    }

    private void addSheet(String text, View.OnClickListener listener) {
        sheetTextList.add(text);
        sheetListenerList.add(listener);
    }

    public void setCancel(String text) {
        this.cancel = text;
    }

    public void setCancelHeight(int height) {
        this.cancelHeight = dp2px(height);
    }

    public void setCancelTextColor(int color) {
        this.cancelTextColor = color;
    }

    public void setCancelTextSize(int textSize) {
        this.cancelTextSize = textSize;
    }

    public void setSheetHeight(int height) {
        this.sheetHeight = dp2px(height);
    }

    public void setSheetTextColor(int color) {
        this.sheetTextColor = color;
    }

    public void setSheetTextSize(int textSize) {
        this.sheetTextSize = textSize;
    }

    public void setTitle(String text) {
        this.title = text;
    }

    public void setTitleHeight(int height) {
        this.titleHeight = height;
    }

    public void setTitleTextColor(int color) {
        this.titleTextColor = color;
    }

    public void setTitleTextSize(int textSize) {
        this.titleTextSize = textSize;
    }

    public void setMargin(int bottom) {
        this.marginBottom = dp2px(bottom);
    }

    public void addCancelListener(View.OnClickListener listener) {
        this.cancelListener = listener;
    }

    private int dp2px(float dipValue) {
        float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }

    public static class DialogBuilder {
        ActionSheet dialog;

        public DialogBuilder(Context context) {
            dialog = new ActionSheet(context);
        }

        /**
         * 添加一個選擇項
         * @param text 選擇項文字
         * @param listener 選擇項點擊回調監聽
         * @return 當前DialogBuilder
         */
        public DialogBuilder addSheet(String text, View.OnClickListener listener) {
            dialog.addSheet(text, listener);
            return this;
        }

        /**
         * 設置取消按鈕文字
         * @param text 文字內容
         * @return 當前DialogBuilder
         */
        public DialogBuilder setCancel(String text) {
            dialog.setCancel(text);
            return this;
        }

        /**
         * 設置取消按鈕高度
         * @param height 高度值,單位dp
         * @return 當前DialogBuilder
         */
        public DialogBuilder setCancelHeight(int height) {
            dialog.setCancelHeight(height);
            return this;
        }

        /**
         * 設置取消按鈕文字顏色
         * @param color 顏色值
         * @return 當前DialogBuilder
         */
        public DialogBuilder setCancelTextColor(int color) {
            dialog.setCancelTextColor(color);
            return this;
        }

        /**
         * 設置取消按鈕文字大小
         * @param textSize 大小值,單位sp
         * @return 當前DialogBuilder
         */
        public DialogBuilder setCancelTextSize(int textSize) {
            dialog.setCancelTextSize(textSize);
            return this;
        }

        /**
         * 設置選擇項高度
         * @param height 高度值,單位dp
         * @return 當前DialogBuilder
         */
        public DialogBuilder setSheetHeight(int height) {
            dialog.setSheetHeight(height);
            return this;
        }

        /**
         * 設置選擇項文字顏色
         * @param color 顏色值
         * @return 當前DialogBuilder
         */
        public DialogBuilder setSheetTextColor(int color) {
            dialog.setSheetTextColor(color);
            return this;
        }

        /**
         * 設置選擇項文字大小
         * @param textSize 大小值,單位sp
         * @return 當前DialogBuilder
         */
        public DialogBuilder setSheetTextSize(int textSize) {
            dialog.setSheetTextSize(textSize);
            return this;
        }

        /**
         * 設置標題
         * @param text 文字內容
         * @return 當前DialogBuilder
         */
        public DialogBuilder setTitle(String text) {
            dialog.setTitle(text);
            return this;
        }

        /**
         * 設置標題欄高度
         * @param height 高度值,單位dp
         * @return 當前DialogBuilder
         */
        public DialogBuilder setTitleHeight(int height) {
            dialog.setTitleHeight(height);
            return this;
        }

        /**
         * 設置標題顏色
         * @param color 顏色值
         * @return 當前DialogBuilder
         */
        public DialogBuilder setTitleTextColor(int color) {
            dialog.setTitleTextColor(color);
            return this;
        }

        /**
         * 設置標題大小
         * @param textSize 大小值,單位sp
         * @return 當前DialogBuilder
         */
        public DialogBuilder setTitleTextSize(int textSize) {
            dialog.setTitleTextSize(textSize);
            return this;
        }

        /**
         * 設置彈出框距離底部的高度
         * @param bottom 距離值,單位dp
         * @return 當前DialogBuilder
         */
        public DialogBuilder setMargin(int bottom) {
            dialog.setMargin(bottom);
            return this;
        }

        /**
         * 設置取消按鈕的點擊回調
         * @param listener 回調監聽
         * @return
         */
        public DialogBuilder addCancelListener(View.OnClickListener listener) {
            dialog.addCancelListener(listener);
            return this;
        }

        /**
         * 創建彈出框,放在最後執行
         * @return 創建的 ActionSheet 實體
         */
        public ActionSheet create() {
            return dialog.createDialog();
        }
    }
}

稍微說一下,依然是建造者模式的寫法,內部類DialogBuilder是一個建造器,調用的時候通過新建一個DialogBuilder實體,設置好想要的參數,最後調用其create方法就建造完成了。
 
最後的最後,終於可以用了!MainActivity.class和activity_main.xml貼上:
MainActivity.class:
public class MainActivity extends Activity {
    private Button button;

    private ActionSheet actionSheet;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();
        initView();
    }

    private void initData() {

    }

    private void initView() {
        button = (Button) findViewById(R.id.button);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showSheet();
            }
        });
    }

    private void showSheet() {
        actionSheet=new ActionSheet.DialogBuilder(this)
                .addSheet("雷軍", new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(MainActivity.this, "當然是雷布斯!", Toast.LENGTH_SHORT).show();
                        actionSheet.dismiss();
                    }
                })
                .addSheet("馬化騰", new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(MainActivity.this, "當然是小馬哥!", Toast.LENGTH_SHORT).show();
                        actionSheet.dismiss();
                    }
                })
                .addSheet("馬雲", new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(MainActivity.this, "當然是馬爸爸!", Toast.LENGTH_SHORT).show();
                        actionSheet.dismiss();
                    }
                })
                .addCancelListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(MainActivity.this, "容我三思", Toast.LENGTH_SHORT).show();
                        actionSheet.dismiss();
                    }
                })
                .create();
    }
}

activity_main.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="center_horizontal"
    android:orientation="vertical">

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_margin="10dp"
        android:text="SHOW SHEET" />

</LinearLayout>

簡單的說一下,調用方法之前已經說了,其中addSheet方法每調用一次就會添加一條選線欄,需要輸入選項欄文字,和點擊的回調監聽,這裏監聽直接用的是View下的OnClickListener,是不是很親切?順帶一提,取消按鈕是默認存在的,所以務必設置一下addCancelListener,不然點擊取消會沒反應。

來看一下效果,運行走起! 

                                                                 

控件支持添加標題欄,改變文字大小、顏色什麼的,幾行代碼搞定,如下:

actionSheet=new ActionSheet.DialogBuilder(this)
        ...

        .setTitle("你覺得以下誰最帥?")
        .setCancel("容我三思")
        .setTitleTextColor(Color.parseColor("#ff69b4"))
        .setCancelTextColor(Color.parseColor("#aaaaaa"))
        .setSheetTextColor(Color.parseColor("#1e90ff"))
        
        ...
        .create();

再次運行,看一下效果:

                                                                

是不是很簡單?更多設置選項在控件源碼裏都已經註釋清楚了,大家可以自己試一試。


總結一下控件的用法就是:new一個DialogBulder,根據需要set一堆東西有幾個選項卡就調用幾次addSheet,最後create一下,搞定。


最後附上源碼地址:點擊打開鏈接


這次的內容就到這裏,我們下次再見。








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章