481
技术社区[云栖]
创新源于模仿之五:做一个自己的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