創新源於模仿之五:做一個自己的QuickAction
有一段時間沒有上來折騰這個專題了,一來項目的確緊張,二來自己一慣是很懶的。今天想與大家分享的是一個QuickAction的東西,模樣其實就是通訊錄中點擊頭像後彈出的那個提供可操作按鈕的窗口。
這個效果其實我們也用過,就是QuickContactBadge。顯然,它很有意思,但是為什麼隻能由係統決定上麵的按鈕呢?
所以今天我們要做的事情就是做一個自己的QuickAction類。
第一步:收集資源
去Android的源代碼網站 https://android.git.kernel.org/ 下載 Contacts 應用的源代碼。在它的drawable目錄裏可以找到那些以quickcontacts_XXXXX的圖片,就是我們需要用
來構造這個彈出窗口的圖片資源。在layout目錄裏有quickcontact.xml則是我們需要的布局文件模板。當然,你也可以參考看看src/.../QuickContactsWindows.java文件。因為裏
麵就有今天我們要實現的內容,隻是它太複雜了,已超出我們想要的功能。
第二步:設計布局
我們的布局文件quickaction.xml如下:
<?xml version="1.0" encoding="UTF-8"?> <RelativeLayout xmlns:andro android:layout_width="fill_parent" android:layout_height="wrap_content" > <FrameLayout android: android:background="@drawable/quickcontact_top_frame" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginTop="10.0dip" /> <ImageView android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/quickcontact_arrow_up" /> <HorizontalScrollView android: android:background="@drawable/quickcontact_slider_background" android:scrollbars="none" android:fadingEdgeLength="0.0dip" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_below="@id/qa_header"> <LinearLayout android:orientation="horizontal" android: android:paddingTop="4.0dip" android:paddingBottom="4.0dip" android:layout_width="wrap_content" android:layout_height="wrap_content"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/quickcontact_slider_grip_left" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/quickcontact_slider_grip_right" /> </LinearLayout> </HorizontalScrollView> <FrameLayout android: android:background="@drawable/quickcontact_bottom_frame" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_below="@id/qa_scroll" /> <ImageView android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="-1.0dip" android:src="@drawable/quickcontact_arrow_down" android:layout_below="@id/qa_footer" /> </RelativeLayout>
裏麵很多東西都好理解,無非就是把圖片堆砌一下罷了。重點說說放在中間的那個HorizontalScrollView裏麵的那個叫qa_tracks的LinearLayout。我們將來的所有按鈕都是放在它
上麵的,現在已經放了兩個按鈕(就是兩邊帶點點的裝飾按鈕)。
第三步:現在開始寫代碼了,封裝按鈕的動作
代碼超簡單,隻需要兩樣東西:圖標和事件監聽器。
第四步:現在可以做QuickAction了
其實原理就是利用PopupWindow來顯示quickaction布局文件,構造多個ActionItem實例放在qa_tracks裏麵就OK了。
具體實現如下:
public class QuickAction { private static final String TAG="QuickAction"; protected final View anchor; //彈出窗口的錨點 private View root; //彈出窗口的根 public final PopupWindow window; //彈出窗口 protected final WindowManager windowManager; //隻是用來計算屏幕大小的 private ArrayList<ActionItem> actionList; //這裏存放了很多的動作ActionItem private final Context mContext; private final LayoutInflater inflater; private final ImageView mArrowDown; private final ImageView mArrowUp; private ViewGroup mTrack; private final Animation mTrackAnim;//顯示出來時的動畫效果 private Interpolator mAnimationInterpolator = new Interpolator(){ @Override public float getInterpolation(float i) { // TODO Auto-generated method stub //這個插值很重要,彈簧效果哦 final float inner = (i * 1.55f) - 1.1f; return 1.2f - inner * inner; }}; public QuickAction(View v) { this.anchor = v; this.mContext = v.getContext(); this.inflater = LayoutInflater.from(this.mContext); this.window = new PopupWindow(mContext); this.window.setTouchInterceptor(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent event) { // TODO Auto-generated method stub if (event.getAction() == MotionEvent.ACTION_OUTSIDE) dismiss(); return false; } }); windowManager = (WindowManager)mContext.getSystemService("window"); this.actionList = new ArrayList<ActionItem>(); this.root = (ViewGroup)this.inflater.inflate(R.layout.quickaction, null); this.mArrowDown = (ImageView)this.root.findViewById(R.id.qa_arrow_down); this.mArrowUp = (ImageView)this.root.findViewById(R.id.qa_arrow_up); this.window.setContentView(this.root); //這裏的動畫anim文件及style,參考Contacts裏的相應文件 this.mTrackAnim = AnimationUtils.loadAnimation(this.mContext, R.anim.quickcontact); this.mTrackAnim.setInterpolator(mAnimationInterpolator); this.mTrack = (ViewGroup)this.root.findViewById(R.id.qa_tracks); } public void dismiss() { this.window.dismiss(); } private void createActionList() { int i = 1; for(ActionItem ai : this.actionList){ Drawable icon = ai.getIcon(); View.OnClickListener listener=ai.getListener(); View v = getActionItem(icon,listener); v.setFocusable(true); v.setClickable(true); this.mTrack.addView(v,i); i++; } } private View getActionItem(Drawable icon, View.OnClickListener listener) { LinearLayout view = (LinearLayout)this.inflater.inflate(R.layout.action_item, null); ImageView ic = (ImageView)view.findViewById(R.id.ai_icon); if (icon != null){ ic.setImageDrawable(icon); if( listener!=null) view.setOnClickListener(listener); view.setVisibility(View.VISIBLE); } else view.setVisibility(View.GONE); return view; } public void addActionItem(ActionItem item) { this.actionList.add(item); } public void setOnDismissListener(PopupWindow.OnDismissListener listener) { this.window.setOnDismissListener(listener); } protected void preShow() { if (this.root == null) throw new IllegalStateException("setContentView was not called with a view to display."); this.window.setBackgroundDrawable(new BitmapDrawable()); this.window.setWidth(LayoutParams.FILL_PARENT); this.window.setHeight(LayoutParams.WRAP_CONTENT); this.window.setTouchable(true); this.window.setFocusable(true); this.window.setOutsideTouchable(true); this.window.setContentView(this.root); //這裏的動畫anim文件及style,參考Contacts裏的相應文件 this.window.setAnimationStyle(R.style.QuickContactAboveAnimation); } private void showArrow(int resId, int margin) { ImageView v1=this.mArrowDown; ImageView v2=this.mArrowUp; if (resId == R.id.qa_arrow_up){ v1 = this.mArrowUp; v2= this.mArrowDown; } int w = v1.getMeasuredWidth()/2; v1.setVisibility(View.VISIBLE); ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams)v1.getLayoutParams(); params.leftMargin=margin-w; v2.setVisibility(View.INVISIBLE); } public void show() { preShow(); int[] loc = new int[2]; this.anchor.getLocationOnScreen(loc); int i = loc[0]; int j = loc[1]; int w = i + this.anchor.getWidth(); int h = j + this.anchor.getHeight(); Rect localRect = new Rect(i, j, w, h); this.root.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); this.root.measure(MeasureSpec.UNSPECIFIED,MeasureSpec.UNSPECIFIED); int ww = this.root.getMeasuredWidth(); int hh = this.root.getMeasuredHeight(); int dw = this.windowManager.getDefaultDisplay().getWidth(); int xx = ( dw- ww) / 2; int yy = localRect.top - hh + 10; boolean isArrowDown = true; if (hh > (localRect.top - 50)) { yy = localRect.bottom - 10; isArrowDown = false; } { int rid=isArrowDown?R.id.qa_arrow_down:R.id.qa_arrow_up; showArrow(rid, localRect.centerX()); createActionList(); this.window.showAtLocation(this.anchor, Gravity.NO_GRAVITY, xx, yy); //showDropDown(); this.mTrack.startAnimation(this.mTrackAnim); } } }
不多解釋了,注意事項參考代碼中的注釋即可。
那些漂亮的動畫是必不可少的,而這又是GOOGLE已經提供了的,拿來用就是了。
最後的步驟:用它
在你要使用的地方(即原來放badge 位置上,修改成這樣):
<!-- <QuickContactBadge android: android:background="#ffffff" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_alignParentLeft="true" android:layout_centerVertical="true" /> --> <ImageView android: android:background="#ffffff" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_alignParentLeft="true" android:layout_centerVertical="true" />
現在,在你的代碼中手工構造並填寫Action即可:
//省略代碼 //...... //構造ActionItem,設置好圖標及點擊事件監聽器 ActionItem call=new ActionItem(); call.setIcon(mContext.getResources().getDrawable(R.drawable.select_call)); call.setOnClickListener(mToast); //,,,,,, //,,,,,, //QuickContactBadge photo = (QuickContactBadge)convertView.findViewById(R.id.badge); //找到錨點 final ImageView photo= (ImageView)convertView.findViewById(R.id.badge); //省略代碼 //,,,,,, //點擊頭像時彈出QuickAction photo.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { action=new QuickAction(v); action.addActionItem(call); action.addActionItem(imsg); action.addActionItem(detail); action.show(); }});
最後更新:2017-04-02 06:51:59