一.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方法時, 網絡請求還沒完成導致的, 歡迎對這個假設進行反駁)
可以說DialogFragment 能夠實現 Dialog 和 PopupWindow能實現的功能, 何樂而不為呢
二.DialogFragment的創建
1.兩種不同的創建方式
①和②的整體流程都是如此:
① .使用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列表長按 彈出框提示 置頂,刪除
場景一
為了顯得易讀, 我覺得 用① .使用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; } }
場景二
實現的步驟大同小異, 關鍵是布局文件
布局文件由一個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




