Android自定義PopupWindow仿qq加好友彈框和IOS第三方

Abels發表於2017-09-19

前言
最近幾天工作不忙,給夥伴們推薦兩款UI。
效果圖:

#仿IOS複製程式碼

1.匯入jar
compile 'com.mylhyl:circleDialog:2.1.6'


2.佈局activity_main.xml複製程式碼
<Button
        android:id="@+id/button"
        android:layout_width="300dp"
        android:layout_height="50dp"
        android:layout_marginTop="100dp"
        android:layout_centerHorizontal="true"
        android:text="點選特效" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:background="#f00"
        android:text="按專案名稱"
        android:textSize="20sp" />複製程式碼


實現程式碼:

 Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//                new CircleDialog.Builder(MainActivity.this)
//                        .setTitle("標題")
//                        .setText("確定要退出")
//                        .setPositive("確定", null)
//                        .show();

                final String[] items = {"拍照", "從相簿選擇", "小視訊"};
                new CircleDialog.Builder(MainActivity.this)
                        .configDialog(new ConfigDialog() {
                            @Override
                            public void onConfig(DialogParams params) {
                                //增加彈出動畫
//                                params.animStyle = R.style.dialogWindowAnim;
                            }
                        })
                        .setTitle("標題")
                        .setTitleColor(Color.BLUE)
                        .setItems(items, new AdapterView.OnItemClickListener() {
                            @Override
                            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                                Toast.makeText(MainActivity.this,items[position],Toast.LENGTH_SHORT).show();
                            }
                        })
                        .setNegative("取消", null)
                        .configNegative(new ConfigButton() {
                            @Override
                            public void onConfig(ButtonParams params) {
                                //取消按鈕字型顏色
                                params.textColor = Color.RED;
                            }
                        })
                        .show();
            }
        });複製程式碼
就這樣就輕鬆實現,是不是很簡單方便。
特別注意,這裡面有一個坑點,如果使用CircleDialog的話,就是需要在AndroidManifest.xml中做配置

    <meta-data android:name="design_width" android:value="1200"/>
    <meta-data android:name="design_height" android:value="1920"/>

只有這樣才可以顯示彈框,這裡與AutoLayout使用很像 複製程式碼

#自定義

#廢話不多說,直接上程式碼複製程式碼

1.定義item_hint_popupwindow.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <View
        android:id="@+id/v_line"
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="#e6e6e6" />
    <TextView
        android:id="@+id/tv_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="15sp"
        android:textColor="#333"
        android:paddingLeft="15dp"
        android:paddingRight="15dp"
        android:paddingTop="10dp"
        android:paddingBottom="10dp"
        android:text="aaaaaaaa"
        android:layout_gravity="center"/>

</LinearLayout>複製程式碼

2.定義item_root_hintpopupwindow.xml

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

    <View
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#66333333"/>
    <LinearLayout
        android:id="@+id/linearLayout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingTop="10dp"
        android:background="@drawable/bg2" />複製程式碼

3.在drawable中新增 .9圖

4.建立一個java類

public class HintPopupWindow {

    private Activity activity;
    private WindowManager.LayoutParams params;
    private boolean isShow;
    private WindowManager windowManager;
    private ViewGroup rootView;
    private ViewGroup linearLayout;

    private final int animDuration = 250;//動畫執行時間
    private boolean isAniming;//動畫是否在執行
    private TextView mianText;
    /**
     * @param contentList 點選item的內容文字
     * @param clickList 點選item的事件
     * 文字和click事件的list是對應繫結的
     */
    public HintPopupWindow(Activity activity, List<String> contentList, List<View.OnClickListener> clickList,setOnClickListener listener){

        this.activity = activity;
        windowManager = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE);

        initLayout(contentList, clickList,listener);
    }

    /**
     * @param contentList 點選item內容的文字
     * @param clickList 點選item的事件
     * @setOnClickListener listener
     */
    public void initLayout(final List<String> contentList, List<View.OnClickListener> clickList,final setOnClickListener listener){

        //這是根佈局
        rootView = (ViewGroup) View.inflate(activity, R.layout.item_root_hintpopupwindow, null);
        linearLayout = (ViewGroup) rootView.findViewById(R.id.linearLayout);

        //格式化點選item, 將文字和click事件一一繫結上去
        final List<View> list = new ArrayList<>();
        for(int x=0; x<contentList.size(); x++){
            View view = View.inflate(activity, R.layout.item_hint_popupwindow, null);
            TextView textView = (TextView) view.findViewById(R.id.tv_content);
            View v_line = view.findViewById(R.id.v_line);
            textView.setText(contentList.get(x));
            final int finalX = x;

            textView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    dismissPopupWindow();
                    listener.OnClickListener(finalX);
                    mianText.setText(contentList.get(finalX));
                    Toast.makeText(activity,"="+contentList.get(finalX),Toast.LENGTH_SHORT).show();
                }
            });
            linearLayout.addView(view);
            list.add(view);
            if(x == 0){
                v_line.setVisibility(View.INVISIBLE);
            }else{
                v_line.setVisibility(View.VISIBLE);
            }
        }
        for (int x=0; x<list.size(); x++){
            list.get(x).setOnClickListener(clickList.get(x));

        }

        //這裡給你根佈局設定背景透明, 為的是讓他看起來和activity的佈局一樣
        params = new WindowManager.LayoutParams();
        params.width = WindowManager.LayoutParams.MATCH_PARENT;
        params.height = WindowManager.LayoutParams.MATCH_PARENT;
        params.format = PixelFormat.RGBA_8888;//背景透明
        params.gravity = Gravity.LEFT | Gravity.TOP;

        //當點選根佈局時, 隱藏
        rootView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismissPopupWindow();
            }
        });

        rootView.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                //如果是顯示狀態那麼隱藏檢視
                if(keyCode == KeyEvent.KEYCODE_BACK && isShow) dismissPopupWindow();
                return isShow;
            }
        });
    }
    public void setData(TextView textView){
        this.mianText = textView;
    }
    /**
     * 彈出選項彈窗
     * @param locationView 預設在該view的下方彈出, 和popupWindow類似
     */
    public void showPopupWindow(View locationView){
        Log.i("Log.i", "showPopupWindow: "+isAniming);
        if(!isAniming) {
            isAniming = true;
            try {
                //這個步驟是得到該view相對於螢幕的座標, 注意不是相對於父佈局哦!
                int[] arr = new int[2];
                locationView.getLocationOnScreen(arr);
                linearLayout.measure(0, 0);
                Rect frame = new Rect();
                activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);//得到狀態列高度
                float x = arr[0] + locationView.getWidth() - linearLayout.getMeasuredWidth();
                float y = arr[1] - frame.top + locationView.getHeight();
                linearLayout.setX(x);
                linearLayout.setY(y);

            /*捕獲當前activity的佈局檢視, 因為我們要動態模糊, 所以這個佈局一定要是最新的,
            *這樣我們把模糊後的佈局蓋到螢幕上時, 才能讓使用者感覺不出來變化*/
                View decorView = activity.getWindow().getDecorView().findViewById(android.R.id.content);
                Bitmap bitmap = getBitmapByView(decorView);//這裡是將view轉成bitmap
                setBlurBackground(bitmap);//這裡是模糊圖片, 這個是重點我會單獨講的, 因為效率很重要啊!!!

                //這裡就是使用WindowManager直接將我們處理好的view新增到螢幕最前端
                windowManager = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE);
                windowManager.addView(rootView, params);

                //這一步就是有回彈效果的彈出動畫, 我用屬性動畫寫的, 很簡單
                showAnim(linearLayout, 0, 1, animDuration, true);

                //檢視被彈出來時得到焦點, 否則就捕獲不到Touch事件
                rootView.setFocusable(true);
                rootView.setFocusableInTouchMode(true);
                rootView.requestFocus();
                rootView.requestFocusFromTouch();

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 得到bitmap點陣圖, 傳入View物件
     */
    public static Bitmap getBitmapByView(View view) {

        Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
        view.draw(new Canvas(bitmap));
        return bitmap;
    }

    private void setBlurBackground(Bitmap bitmap) {
        rootView.setAlpha(0);
        alphaAnim(rootView, 0, 1, animDuration);
    }



    public void dismissPopupWindow(){
        Log.i("Log.i", "dismissPopupWindow: "+isAniming);
        if(!isAniming) {
            isAniming = true;
            isShow = false;
            goneAnim(linearLayout, 0.95f, 1, animDuration / 3, true);
        }
    }

    public WindowManager.LayoutParams getLayoutParams(){
        return params;
    }

    public ViewGroup getLayout(){
        return linearLayout;
    }

    /**
     * popupwindow是否是顯示狀態
     */
    public boolean isShow(){
        return isShow;
    }

    private void alphaAnim(final View view, int start, int end, int duration){

        ValueAnimator va = ValueAnimator.ofFloat(start, end).setDuration(duration);
        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue();
                view.setAlpha(value);
            }
        });
        va.start();
    }

    private void showAnim(final View view, float start, final float end, int duration, final boolean isWhile) {

        ValueAnimator va = ValueAnimator.ofFloat(start, end).setDuration(duration);
        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue();
                view.setPivotX(view.getWidth());
                view.setPivotY(0);
                view.setScaleX(value);
                view.setScaleY(value);
            }
        });
        va.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                if (isWhile) {
                    showAnim(view, end, 0.95f, animDuration / 3, false);
                }else{
                    isAniming = false;
                }
            }
        });
        va.start();
    }

    public void goneAnim(final View view, float start, final float end, int duration, final boolean isWhile){

        ValueAnimator va = ValueAnimator.ofFloat(start, end).setDuration(duration);
        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue();
                view.setPivotX(view.getWidth());
                view.setPivotY(0);
                view.setScaleX(value);
                view.setScaleY(value);
            }
        });
        va.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                if(isWhile){
                    alphaAnim(rootView, 1, 0, animDuration);
                    goneAnim(view, end, 0f, animDuration, false);
                }else{
                    try {
                        windowManager.removeViewImmediate(rootView);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    isAniming = false;
                }
            }
        });
        va.start();
    }複製程式碼

4.定義一個interface 回撥點選的資料

public interface setOnClickListener {
    void OnClickListener(int finalX);
}複製程式碼

5.在MainActivity中直接使用

public class MainActivity extends AppCompatActivity {

    private HintPopupWindow hintPopupWindow;
    private TextView imageView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView = (TextView) findViewById(R.id.textView);
        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //彈出選項彈窗
                hintPopupWindow.showPopupWindow(v);
                hintPopupWindow.setData(imageView);
            }
        });

        //初始化彈出資料
        ArrayList<String> strList = new ArrayList<>();
        strList.add("選項item1");
        strList.add("選項item2");
        strList.add("選項item3");

        ArrayList<View.OnClickListener> clickList = new ArrayList<>();
        View.OnClickListener clickListener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//                Toast.makeText(MainActivity.this, "點選事件觸發", Toast.LENGTH_SHORT).show();
            }
        };
        clickList.add(clickListener);
        clickList.add(clickListener);
        clickList.add(clickListener);

        //具體初始化
        hintPopupWindow = new HintPopupWindow(this, strList, clickList, new setOnClickListener() {
            @Override
            public void OnClickListener(int finalX) {
                int tag = finalX+1;
                Log.e("tag","=====finalX===下標====="+tag);
            }
        });
    }
}複製程式碼

#總結
由於簡單,專案地址就不發連結,大家按照步驟就可以實現。希望對各位有所幫助。如果有問題,歡迎大家評論

相關文章