DialogFragment的應用


 

一.DialogFragment簡單介紹:

1.基本概念

DialogFrament 指一個與fragment建立了關聯的Dialog, 隨fragment生, 隨fragment死, 即Dialog隨Fragment的生命周期而變.

2.使用背景

像常規的Dialog或者是Popupwindow, 都依附於Activity而存在, 如果Activity突然銷毀了, 在Dialog和Popupwindow已經show的情況下, 程序會報android.view.WindowLeaked, 當然也是有辦法解決的, 在Activity的onDestroy方法中判斷不為空時dismiss即可

DialogFrament的優點有 ① 保持和Fragment相同的生命周期 ② 屏幕旋轉相關數據不會被銷毀 ③ 像Activity,Fragment一樣網絡請求 (英語趣配音這個App 在點擊1請求得到單詞發音,如果還沒響應,再點擊2返回時, 會一直卡在那里, 應該是線程阻塞了,導致這個原因很可能 是此App的彈框用的是PopupWindow, 然后在 PopupWindow中做網絡請求了, 在調用 onBackPressed方法時, 網絡請求還沒完成導致的, 歡迎對這個假設進行反駁

O66LVWO2J((RU%H0CG[@A~T

可以說DialogFragment 能夠實現 Dialog 和 PopupWindow能實現的功能, 何樂而不為呢

二.DialogFragment的創建

1.兩種不同的創建方式

①和②的整體流程都是如此:

clip_image003

① .使用onCreateView

在onCreate中接收傳遞過來的參數

在onCreateView中創建布局文件

在onViewCreated中對布局控件處理

在onStart中設置布局在屏幕中的屬性

使用展示Fragment的方式展示DialogFragment

這里將重點講下onStart方法中的操作

為什么不在onCreateView中對布局在屏幕中的位置,寬高進行設置的,而是要在onStart中操作?

其實看下DialogFragment 的onStart()的源碼會發現

@Override
    public void onStart() {
        super.onStart();
        if (mDialog != null) {
            mViewDestroyed = false;
            mDialog.show();
        }
    }

注意mDialog.show();

回想下當初是怎么設置自定義Dialog的位置和寬高的,是不是先調用show方法,再進行設置Dialog位置和寬高才有效, 同理這里也是如此; 因此就不能再onCreateView中設置Dialog的布局屬性了.

可以以BottomDialog作為參考

還有個問題,如何DialogFragment在屏幕中的位置

和Dialog一樣,DialogFragment默認是在屏幕中心, 如果你想把他設置在某個具體的位置, 可以把它先設置到坐標(0,0)的位置, 然后設置DialogFragment離左邊的距離是多少,離頂部的距離是多少

@Override
    public void onStart() {
        super.onStart();
        Window window = getDialog().getWindow();
        WindowManager.LayoutParams params = window.getAttributes();
        params.gravity = Gravity.TOP | Gravity.LEFT;
        params.y = mTop;
        params.x = mLeft;
        window.setAttributes(params);
        window.setBackgroundDrawable(new ColorDrawable(Color.parseColor("#00000000")));
    }

可以這么說,DialogFragment 就是 Dialog套了一層 Fragment的皮, 源碼也不長,真值得研究

②. onCreateDialog(Bundle)

可以做從onCreate到onStart的事, 可以說是一步到位

三.DialogFragment應用場景

1. 底部彈出框 如分享/打開相冊+底部彈出動畫

2. 請求網絡加載中,清理緩存如ProgressBar

3. QQ列表長按 彈出框提示 置頂,刪除

 

場景一

image

為了顯得易讀, 我覺得 用① .使用onCreateView比較好

這里有個問題沒搞清楚?

public boolean onTouch(View v, MotionEvent event) { dismiss(); return true; }

return true 彈出動畫會執行

return false 彈出動畫就不執行, 哪位高人知道的還望指點

 
public class BottomDialog extends DialogFragment {

    private String mTitle;
    private String mCancle;
    private String[] mItems;
    private BottomDialogListener mListener;
    private boolean mIsDismiss = false;
    private View mRootView;

    public static BottomDialog newInstance(String title, String cancle, String[] items) {
        BottomDialog dialog = new BottomDialog();
        // Supply as arguments.
        Bundle args = new Bundle();
        args.putString("title", title);
        args.putString("cancle", cancle);
        args.putStringArray("items", items);
        dialog.setArguments(args);
        return dialog;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mTitle = getArguments().getString("title");
        mCancle = getArguments().getString("cancle");
        mItems = getArguments().getStringArray("items");
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        Log.e("Wislie", "onCreateView");
        getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
        mRootView = inflater.inflate(R.layout.dialog_bottom, container, false);
        mRootView.startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.fade_in));
        return mRootView;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Log.e("Wislie", "onCreateDialog");

        TextView title = (TextView) view.findViewById(R.id.title);
        ListView listView = (ListView) view.findViewById(R.id.listview);
        TextView cancel = (TextView) view.findViewById(R.id.cancel);
        title.setText(mTitle);
        listView.setAdapter(new ArrayAdapter(getActivity(),
                android.R.layout.simple_list_item_1, android.R.id.text1, mItems));
        cancel.setText(mCancle);

        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                if (mListener != null) {
                    mListener.onItemClick(position);
                }
            }
        });

        cancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismiss();
                if (mListener != null) {
                    mListener.onConfirm();
                }

            }
        });
    }

    @Override
    public void onStart() {
        super.onStart();
        Log.e("Wislie", "onStart");
        Window window = getDialog().getWindow();
        WindowManager.LayoutParams params = window.getAttributes();

        params.gravity = Gravity.BOTTOM;
        params.width = WindowManager.LayoutParams.MATCH_PARENT;
        window.setAttributes(params);

        window.setBackgroundDrawable(new ColorDrawable(Color.parseColor("#00000000")));
        window.getDecorView().setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                dismiss();
                return true;
            }
        });
    }

    /*
    * 也可以通過 onCreateDialog 來創建Dialog
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        Dialog dialog = new Dialog(getActivity());
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        dialog.setContentView(R.layout.dialog_bottom);
        Window window = dialog.getWindow();
        WindowManager.LayoutParams params = window.getAttributes();

        params.gravity = Gravity.BOTTOM;
        params.width = WindowManager.LayoutParams.MATCH_PARENT;
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        window.setAttributes(params);
        return dialog;
    }*/

    /**
     *  彈出對話框
     * @param fragmentManager
     */
    public void showDialog(FragmentManager fragmentManager) {
        if(this == null) return;

        // DialogFragment.show() will take care of adding the fragment
        // in a transaction.  We also want to remove any currently showing
        // dialog, so make our own transaction and take care of that here.
        FragmentTransaction ft = fragmentManager.beginTransaction();
        Fragment prev = fragmentManager.findFragmentByTag(getTag());
        if (prev != null) {
            ft.remove(prev);
        }
        ft.addToBackStack(null);
        // Create and show the dialog.
        show(ft, getTag());
    }

    /**
     * 對話框消失
     */
    @Override
    public void dismiss() {

        if (mIsDismiss) {
            return;
        }
        mIsDismiss = true;

        Animation fadeOutAnim = AnimationUtils.loadAnimation(getActivity(), R.anim.fade_out);
        mRootView.startAnimation(fadeOutAnim);
        fadeOutAnim.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                Log.e("Wislie","animation end");
               BottomDialog.super.dismiss();
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

    }

    public interface BottomDialogListener {
        
        void onConfirm();

        void onItemClick(int position);
    }

    public void setOnBottomDialogListener(BottomDialogListener listener) {
        mListener = listener;
    }
}
 
場景二
image
 
         
實現的步驟大同小異, 關鍵是布局文件
布局文件由一個ProgressBar 和 TextView文本構成, ProgressBar 有個重要的屬性 indeterminateDrawable, 指向一個drawable的 xml文件, 該drawable 實現圍繞自身旋轉, 有多種實現方式
方式1: 
<?xml version="1.0" encoding="utf-8"?>
<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@mipmap/ic_loading_white"
    android:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="360" />
方式2: drawable用幀動畫集合
 
         
 
         
場景三

 

 
         
實現這一效果的代碼 是參考PopupList, 在Github上可以找到這個資源, 這里我是用DialogFragment實現的
關鍵的幾點思路: 1.橫向的布局嵌入TextView; 2.背景點擊和文字點擊狀態的變化; 3.上下三角形的繪制
 
         
代碼看個大概就好了,因為都是差不多的套路
public class HintDialogFragment extends BaseDialogFragment {

    private int mTop;
    private boolean mIsTop;
    private View mRootView;
    private TriangleView mTopArrow;
    private TriangleView mBottomArrow;

    public static HintDialogFragment newInstance(int top, boolean isTop) {
        HintDialogFragment dialog = new HintDialogFragment();
        // Supply as arguments.
        Bundle args = new Bundle();
        args.putInt("top", top);
        args.putBoolean("isTop",isTop);
        dialog.setArguments(args);
        return dialog;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mTop = getArguments().getInt("top");
        mIsTop = getArguments().getBoolean("isTop");
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
        mRootView = inflater.inflate(R.layout.dialog_hint, container, false);
        mTopArrow = (TriangleView) mRootView.findViewById(R.id.top_arrow);
        mBottomArrow = (TriangleView) mRootView.findViewById(R.id.bottom_arrow);
        if(mIsTop){
            ViewUtils.measureView(mTopArrow);

            mTop = mTop - mTopArrow.getMeasuredHeight();
            mTopArrow.setVisibility(View.VISIBLE);
            mBottomArrow.setVisibility(View.GONE);
        }else{
            mTopArrow.setVisibility(View.GONE);
            mBottomArrow.setVisibility(View.VISIBLE);
        }
        return mRootView;
    }


    @Override
    public void onStart() {
        super.onStart();
        Window window = getDialog().getWindow();
        WindowManager.LayoutParams params = window.getAttributes();
        params.gravity = Gravity.TOP;
        params.y = mTop;
        window.setAttributes(params);
        window.setBackgroundDrawable(new ColorDrawable(Color.parseColor("#00000000")));

    }
}
 
         
布局文字是這樣子的 dialog_hint.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:orientation="vertical">

    <wislie.com.widgets_sample.widgets.TriangleView
        android:id="@+id/bottom_arrow"
        android:layout_width="30dp"
        android:layout_height="15dp"
        android:visibility="gone"
        app:is_top="false" />

    <wislie.com.widgets_sample.widgets.listview.HintView
        android:id="@+id/horizontal_listview"
        android:layout_width="wrap_content"
        android:layout_height="40dp" />

    <wislie.com.widgets_sample.widgets.TriangleView
        android:id="@+id/top_arrow"
        android:layout_width="30dp"
        android:layout_height="15dp"
        android:visibility="gone"
        app:is_top="true" />

</LinearLayout>
 
         
看HintView, 這里主要涉及到文字點擊的ColorStateList 和 背景點擊的StateListDrawable
public class HintView extends LinearLayout {

    //TextView的樣式
    private static final int STYLE_LEFT = 0;
    private static final int STYLE_RIGHT = 1;
    private static final int STYLE_BOTH_SIDES = 2;
    private static final int STYLE_NONE = 3;
    //這里的數量可以隨便增加 減少
    private List<String> inputList = Arrays.asList("編輯","刪除","取消","更多");

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

    public HintView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public HintView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {

        setOrientation(HORIZONTAL);
        setGravity(Gravity.CENTER_VERTICAL);
        addData(inputList);
    }

    public void addData(List<String> inputDatas){
        if(inputDatas == null || inputDatas.size() == 0) return;

        // 1  2 多個

        switch (inputDatas.size()){
            case 1:
                createTextView(0, inputDatas.get(0), STYLE_BOTH_SIDES);
                break;
            case 2:
                createTextView(0, inputDatas.get(0), STYLE_LEFT);
                createView();
                createTextView(1, inputDatas.get(1), STYLE_RIGHT);
                break;
            default:
                createTextView(0, inputDatas.get(0), STYLE_LEFT);
                createView();
                for(int i = 1; i < inputDatas.size() - 1; i++) {
                    createTextView(i, inputDatas.get(i), STYLE_NONE);
                    createView();
                }
                createTextView(inputDatas.size() - 1, inputDatas.get(inputDatas.size() - 1), STYLE_RIGHT);
                break;
        }

    }

    private void createTextView(int index, String inputData, int style){
        TextView textView = new TextView(getContext());
        textView.setGravity(Gravity.CENTER);
        textView.setText(inputData);
        //文字點擊
        ColorStateList colorStateList =  getResources().getColorStateList(R.color.press_text);
        textView.setTextColor(colorStateList);

        //背景點擊
        StateListDrawable stateListDrawable = null;
        if(style == STYLE_BOTH_SIDES){
            stateListDrawable = (StateListDrawable) getResources().getDrawable(R.drawable.press_bg_round);
        }else if(style == STYLE_LEFT){
            stateListDrawable = (StateListDrawable) getResources().getDrawable(R.drawable.press_bg_left_round);
        }else if(style == STYLE_RIGHT){
            stateListDrawable = (StateListDrawable) getResources().getDrawable(R.drawable.press_bg_right_round);
        }else if(style == STYLE_NONE){
            stateListDrawable = (StateListDrawable) getResources().getDrawable(R.drawable.press_bg_rect);
        }

        textView.setBackground(stateListDrawable);
        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
        params.gravity = Gravity.CENTER;
        textView.setPadding(10, 10, 10, 10);
        textView.setLayoutParams(params);
        textView.setTag(index);
        textView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                int index = (int) v.getTag();
                Toast.makeText(getContext(),inputList.get(index), Toast.LENGTH_SHORT).show();
            }
        });
        addView(textView);
    }

    private void createView(){
        View view = new View(getContext());
        LayoutParams params = new LayoutParams(1, LayoutParams.MATCH_PARENT);
        view.setLayoutParams(params);
        view.setBackgroundColor(Color.parseColor("#ffffff"));
        addView(view);
    }
}
 
         
TriangleView 三角形的繪制看看就好
public class TriangleView extends View {

    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private Path mPath = new Path();

    private float[][] mPointsBottom = new float[3][2];
    private float[][] mPointsTop = new float[3][2];

    private boolean mIsTop = false;
    public TriangleView(Context context) {
        super(context);
    }

    public TriangleView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public TriangleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TriangleView, defStyleAttr, 0);
        mIsTop = typedArray.getBoolean(R.styleable.TriangleView_is_top, mIsTop);

    }

    private void init(){
        if(getWidth() == 0 || getHeight() == 0) return;
        int width = getWidth();
        int height = getHeight();
        if(mIsTop){
            mPointsTop[0][0] = 0;
            mPointsTop[0][1] = 0;
            mPointsTop[1][0] = width;
            mPointsTop[1][1] = 0;
            mPointsTop[2][0] = width/2;
            mPointsTop[2][1] = height;
        }else{
            mPointsBottom[0][0] = width/2;
            mPointsBottom[0][1] = 0;
            mPointsBottom[1][0] = width;
            mPointsBottom[1][1] = height;
            mPointsBottom[2][0] = 0;
            mPointsBottom[2][1] = height;
        }

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        init();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(getWidth() == 0 || getHeight() == 0) return;
        mPaint.setColor(Color.parseColor("#585858"));
        mPaint.setStyle(Paint.Style.FILL);
        if(mIsTop){
            mPath.moveTo(mPointsTop[0][0], mPointsTop[0][1]);
            mPath.lineTo(mPointsTop[1][0], mPointsTop[1][1]);
            mPath.lineTo(mPointsTop[2][0], mPointsTop[2][1]);
            mPath.close();
        }else{
            mPath.moveTo(mPointsBottom[0][0], mPointsBottom[0][1]);
            mPath.lineTo(mPointsBottom[1][0], mPointsBottom[1][1]);
            mPath.lineTo(mPointsBottom[2][0], mPointsBottom[2][1]);
            mPath.close();
        }
        canvas.drawPath(mPath, mPaint);
    }
}
 
 
在這里, 萬分抱歉, 代碼沒有寫明注釋~~~
github鏈接地址:https://github.com/A18767101271/widgets_sample 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM