下面我们把这个控件内嵌到Layout中做一些动画和展示,效果图:
这个子控件可以上下移动,可以左右滑动,如果上下滑动距离大于左右滑动距离,则必须上下滑动
这样来写onTouch事件:
@Override public boolean onTouchEvent(MotionEvent ev) { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(ev); int mScrollX = this.getScrollX(); final int action = ev.getAction(); final float x = ev.getX(); final float y = ev.getY(); switch (action) { case MotionEvent.ACTION_DOWN: mFlagLimitMove = false; mFlagLimitUp = false; /* * If being flinged and user touches, stop the fling. isFinished * will be false if being flinged. */ if (!mScroller.isFinished()) { mScroller.abortAnimation(); } // Remember where the motion event started mOriMotionX = x; mOriMotionY = y; mLastMotionX = x; mLastMotionY = y; mTimeDown = System.currentTimeMillis(); mLastDownX = x; return true; case MotionEvent.ACTION_MOVE: //如果是往后滑动,屏幕向前,那么mLastMotionX是比x大的,deltaX是正的 int deltaX = (int) (mLastMotionX - x); int deltaY = (int) (mLastMotionY - y); // final int moveX = (int) (mOriMotionX - x); final int moveY = (int) (mOriMotionY - y); mLastMotionX = x; mLastMotionY = y; //X方向的移动更多 if (Math.abs(deltaY) < Math.abs(deltaX)) { //虽然X方向移动更多,但由于已经判定为上下滑动,所以只能return true,做这种情况左右不能滑动 if (mFlagLimitUp) { return true; } final int buffer = getWidth() / 2; if (deltaX < 0) { if (deltaX < -MOVEY_NUM_X) { //mFlagLimitMove代表X方向确认在滑动了 mFlagLimitMove = true; } if (mCurrentScreen == 0 && !mExecuteWhileSnapOut) { System.out.println("=====deltaX="+deltaX); deltaX = getSlowDownDelta(deltaX); System.out.println("=====after deltaX="+deltaX); } System.out.println("====mScrollX="+mScrollX+"buffer="+buffer+"deltaX="+deltaX+"x="+(Math.max(-mScrollX - buffer, deltaX))); scrollBy(Math.max(-mScrollX - buffer, deltaX), 0); // } } else if (deltaX > 0) { int availableToScroll = 0; if (getChildCount() > 0) { //此时Workspace上可能未加任何item,count == 0 availableToScroll = getChildAt( getChildCount() - 1).getRight() - mScrollX - getWidth(); } if (deltaX > MOVEY_NUM_X) { mFlagLimitMove = true; } if (mCurrentScreen == getChildCount() - 1 && !mExecuteWhileSnapOut) { deltaX = getSlowDownDelta(deltaX); } System.out.println("====availableToScroll="+availableToScroll+"deltaX="+deltaX); scrollBy(Math.min(availableToScroll + buffer, deltaX), 0); } } else { //如果已经判定为上下滑动或 ,当时X方向也有移动,这种情况也不处理 if (mFlagLimitMove || mFlagLimitUp) { return true; } //deltaY>0说明为往上滑动 if (deltaY > 0 && moveY > MOVEY_NUM) { if (mHandler != null) { mFlagLimitUp = true; mHandler.sendMessage(mHandler .obtainMessage(DingLayout.MESSAGE_MOVING_UP)); } } else if (deltaY < 0 && moveY < -MOVEY_NUM) { if (mHandler != null) { mFlagLimitUp = true; mHandler.sendMessage(mHandler .obtainMessage(DingLayout.MESSAGE_MOVING_DOWN)); } } } return true; case MotionEvent.ACTION_UP: final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(DEFAULT_VALUE, mMaximumVelocity); int velocityX = (int) velocityTracker.getXVelocity(); int velocityY = (int) velocityTracker.getYVelocity(); System.out.println("====mFlagLimitUp="+mFlagLimitUp+"mFlagLimitMove="+mFlagLimitMove); if (!mFlagLimitUp) { if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) { // Fling hard enough to move left snapToScreen(mCurrentScreen - 1); } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) { // Fling hard enough to move right snapToScreen(mCurrentScreen + 1); } else { snapToDestination(mLastMotionX < mOriMotionX); } } else { snapToDestination(mLastMotionX < mOriMotionX); } mTimeUp = System.currentTimeMillis(); //如果x方向移动非常少,并且没有y方向的移动,我们来判断是不是点击事件 if (!mFlagLimitUp && !mFlagLimitMove) { // 如果点击时间比较短,而且手指没有移动很大位置,则认为为点击事件 if (Math.abs(x - mOriMotionX) < MOVE_LIMIT && Math.abs(y - mOriMotionY) < MOVE_LIMIT && Math.abs(mTimeUp - mTimeDown) < TIME_LIMIT) { if (mHandler != null) { mHandler.sendMessage(mHandler.obtainMessage( DingLayout.MESSAGE_MOVING_CLICK, y)); return true; } } if (velocityY > Y_RATE_LIMIT && Math.abs(velocityX) < X_RATE_LIMIT) { if (mHandler != null) { mHandler.sendMessage(mHandler .obtainMessage(DingLayout.MESSAGE_MOVING_DOWN)); } } else if (velocityY < -Y_RATE_LIMIT && Math.abs(velocityX) < X_RATE_LIMIT) { if (mHandler != null) { mHandler.sendMessage(mHandler .obtainMessage(DingLayout.MESSAGE_MOVING_UP)); } } } // end flag if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } mFlagLimitMove = false; mFlagLimitUp = false; mTouchState = TOUCH_STATE_REST; if (Math.abs(mLastDownX - x) > TEN) { return true; } return false; case MotionEvent.ACTION_CANCEL: mTouchState = TOUCH_STATE_REST; return false; default: break; } return true; }
activity:
布局:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/MainRoot" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <RelativeLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_below="@id/float_MainRoot" > <ImageView android:id="@+id/bgView" android:layout_width="fill_parent" android:layout_height="fill_parent" android:scaleType="centerCrop" android:src="@drawable/search_bg5" /> <com.ringcentral.android.utils.ui.widget.DingLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/dinglayout" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_gravity="left|top" android:background="#00000000" > <RelativeLayout android:id="@+id/ding_updown_layout" android:layout_width="fill_parent" android:layout_height="wrap_content" > <com.ringcentral.android.utils.ui.widget.Workspace android:id="@+id/workspace" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" /> <LinearLayout android:id="@+id/dots_layout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_below="@+id/ding_view" android:layout_centerHorizontal="true" android:layout_marginRight="10dip" android:layout_marginTop="10dip" android:orientation="horizontal" /> </RelativeLayout> </com.ringcentral.android.utils.ui.widget.DingLayout> </RelativeLayout> </LinearLayout>
activity:
public class DingLayoutActivity extends Activity implements OnViewChangedListener { DingLayout mDingLayout; Workspace mWorkspace; /** 用inflater来载入layout. */ private LayoutInflater mInflater; int mTotalDingnum; ImageView mBtnRefresh; private int mCurrentDownUrlIndex = 0; public Handler mHandler = new Handler() { public void handleMessage(Message msg) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.main1); mDingLayout = (DingLayout) findViewById(R.id.dinglayout); mDingLayout.setHandlerFormActivity(this.mHandler); mWorkspace = (Workspace) findViewById(R.id.workspace); mWorkspace.setOnViewChangedListener(this); initialWorkspace(); } /** * 初始化workspace. */ private void initialWorkspace() { mInflater = LayoutInflater.from(this); // 该list为内存中的ding列表,列表中元素存储着ding的A部分数据 DingManager dingMgr = DingManager.getInstance(this); List<DingInfo> list = dingMgr.getDingList(); mTotalDingnum = list.size(); // 遍历列表中的各个元素来构造workspace中的view for (int i = 0; i < mTotalDingnum; i++) { View view = mInflater.inflate(R.layout.ding_item, null); DingInfo info = list.get(i); view.setTag(info.getDingId()); String oldContent = info.getContent(); mWorkspace.addView(view); mBtnRefresh = (ImageView) view.findViewById(R.id.btn_refresh); setViewData(info, view, true); info.setContent(oldContent); // end modify } mDingLayout.updateDots(mTotalDingnum, mCurrentDownUrlIndex); } private void setViewData(DingInfo netdingdata, View view, boolean useTitle) { if (view == null) { return; } TextView title = (TextView) view.findViewById(R.id.title); TextView content = (TextView) view.findViewById(R.id.main_content); TextView description = (TextView) view.findViewById(R.id.description); TextView time = (TextView) view.findViewById(R.id.time); title.setText(netdingdata.getTitle()); // 默认值如果为空,不更新 String updateContent = netdingdata.getContent(); if (!TextUtils.isEmpty(updateContent)) { // modify by qiaopu 用title的view if (useTitle) { title.setText(netdingdata.getContent()); content.setText(""); } else { content.setText(netdingdata.getContent()); } // end modify } description.setText(netdingdata.getDescription()); time.setText(netdingdata.getTime()); } @Override public void onViewChanged(int viewIndex) { if (viewIndex < 0 || viewIndex >= mTotalDingnum) { // Log.d(TAG, "invalid mCurrentDownUrlIndex"); return; } if (mCurrentDownUrlIndex != viewIndex) { View view = mWorkspace.getChildAt(mCurrentDownUrlIndex); if (view != null) { } mCurrentDownUrlIndex = viewIndex; mDingLayout.updateDots(mTotalDingnum, mCurrentDownUrlIndex); } } }
DingLayout:
public class DingLayout extends RelativeLayout { // /** Context */ // private Context mContext; /** MESSAGE_MOVING_UP表示手势的动作为向上滑. */ public static final int MESSAGE_MOVING_UP = 0; /** MESSAGE_MOVING_UP表示手势的动作为向下滑. */ public static final int MESSAGE_MOVING_DOWN = 1; /** MESSAGE_MOVING_UP表示手势的动作为点击. */ public static final int MESSAGE_MOVING_CLICK = 2; /** isUp为判断workspace是否在上方. */ private boolean mIsUp = false; /** isUp为判断滑动的动画是否在进行中. */ private boolean mIsMove = false; /** 可上下滑动的Layout,包括Workspace和点点提示。 */ private ViewGroup mUpDownLayout; /** Workspace. */ private Workspace mSpace; /** 点点提示Workspace当前页的Layout。 */ private LinearLayout mDotsLayout; /** 点点的Y坐标偏移。由于点点需要显示在DING图标下面,而DING图标只有在Workspace的screen中才有,所以用此全局来计算并记住。 */ private int mDotsOffsetY; /** 当前索引。 */ private int mCurrent = -1; /** log tag. */ private static final String TAG = "DingLayout"; /** 动画运行时间. */ private static final int ANIMATIONTIME = 650; /** mHandlerFromMainActivity从main activity传进来. */ private Handler mHandlerFromMainActivity; /** 是否是动画进行中.*/ private boolean isRunning = false; /** Context*/ private Context mContext = null; /** * view的构造函数. * * @param context * context * @param attrs * attrs * */ public DingLayout(Context context, AttributeSet attrs) { super(context, attrs); mIsUp = false; isRunning = false; mContext = context; } /** * @return 为ding在上还是在底下的标识 */ public boolean isUp() { return mIsUp; } /** * 把mainActivity的hander传入这个view. * @param handlerFromMainActivity HandlerFromMainActivity */ public void setHandlerFormActivity(Handler handlerFromMainActivity) { this.mHandlerFromMainActivity = handlerFromMainActivity; } @Override public boolean dispatchTouchEvent(MotionEvent ev) { //如果在动画进行中,不进行event的分发 if (isRunning) { return true; } return super.dispatchTouchEvent(ev); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); //下面两句保证初始化时workspace的位置在上面或是在下面 if (mIsUp) { layoutUp(); } else { layoutDown(); } if (mDotsOffsetY == 0) { View dingView = findViewById(R.id.ding_view); if (dingView != null) { mDotsOffsetY = dingView.getBottom(); System.out.println("=====mDotsOffsetY="+mDotsOffsetY); } } if (mDotsOffsetY > 0) { //需要修改LayoutParams,否则在parent调用Layout的时候,坐标又会被改变。 LayoutParams params = (LayoutParams) mDotsLayout.getLayoutParams(); params.topMargin += mDotsOffsetY; mDotsLayout.layout(mDotsLayout.getLeft(), mDotsLayout.getTop() + mDotsOffsetY, mDotsLayout.getRight(), mDotsLayout.getBottom() + mDotsOffsetY); //只需执行一次。 mDotsOffsetY = -1; } } @Override protected void onFinishInflate() { super.onFinishInflate(); mUpDownLayout = (ViewGroup) findViewById(R.id.ding_updown_layout); mSpace = (Workspace) findViewById(R.id.workspace); mSpace.setHandler(mHandler); //<add by qumiao 2012.3.16 BEGIN //fix bug: SEARHBOX-219 【二期】多个Ding的时候,左右滑动Ding的时候,显示ding位置的点点不应该随ding移动 mSpace.setSnapListener(new WorkspaceSnapListener() { @Override public void onSnapToScreen(Workspace workspace, int whichScreen) { updateDots(getDingCount(), whichScreen); } }); //add by qumiao 2012.3.16 END> mDotsLayout = (LinearLayout) findViewById(R.id.dots_layout); } /** * 更新点点的状态,包括总数和当前位置。 * @param total 总数。 * @param current 当前位置。 */ public void updateDots(int total, int current) { Workspace.updateDots(mDotsLayout, total, current, null); } /** * setCurrentScreen. * @param index Index. */ public void setCurrentScreen(int index) { mSpace.setCurrentScreen(index); updateDots(getDingCount(), index); } /** * set IsUp的值. * @param isUp set mIsUp值 */ public void setIsUp(boolean isUp) { this.mIsUp = isUp; } /** * 获取Ding的数量。 * @return 数量。 */ private int getDingCount() { List<DingInfo> list = DingManager.getInstance(getContext()) .getDingList(); return list.size(); } /** * 向上移的动画. */ private void moveUp() { logCat("moveUp mToppanel.getWidth()=" + mUpDownLayout.getWidth() + "mToppanel.getHeight()=" + mUpDownLayout.getHeight()); layoutUp(); Animation upAnimation = new TranslateAnimation(0, 0, getWorkSpaceDownPosition(), 0); upAnimation.setDuration(ANIMATIONTIME); upAnimation.setAnimationListener(new AnimationListener() { @Override public void onAnimationStart(Animation animation) { mIsMove = true; isRunning = true; } @Override public void onAnimationRepeat(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { mIsMove = false; isRunning = false; if (mHandlerFromMainActivity != null) { Message msg = mHandlerFromMainActivity.obtainMessage(); msg.what = SearchListener.MSG_ANIMATION_DONE; mHandlerFromMainActivity.handleMessage(msg); mHandlerFromMainActivity .sendEmptyMessage(SearchListener.MSG_DING_UP_FINISHED); mHandlerFromMainActivity .sendEmptyMessage(SearchListener.MSG_DING_MOVE_UP); } } }); if (mHandlerFromMainActivity != null) { Message msg = mHandlerFromMainActivity.obtainMessage(); msg.what = SearchListener.MSG_ANIMATION_PREPARE; mHandlerFromMainActivity.handleMessage(msg); } mUpDownLayout.startAnimation(upAnimation); } /** * 向下移的动画. */ private void moveDown() { if (mHandlerFromMainActivity != null) { //<modified by qumiao 2012.2.15 BEGIN //异步改成同步,以免影响动画效果。 // mHandlerFromMainActivity // .sendEmptyMessage(SearchListener.MSG_DING_MOVE_DOWN); Message msg = mHandlerFromMainActivity.obtainMessage(); msg.what = SearchListener.MSG_DING_MOVE_DOWN; mHandlerFromMainActivity.handleMessage(msg); //modified by qumiao END> } logCat("mToppanel.getWidth()=" + mUpDownLayout.getWidth() + "mToppanel.getHeight()=" + mUpDownLayout.getHeight()); layoutDown(); Animation downAnimation = new TranslateAnimation(0, 0, -getWorkSpaceDownPosition(), 0); downAnimation.setDuration(ANIMATIONTIME); downAnimation.setAnimationListener(new AnimationListener() { @Override public void onAnimationStart(Animation animation) { mIsMove = true; isRunning = true; } @Override public void onAnimationRepeat(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { mIsMove = false; isRunning = false; if (mHandlerFromMainActivity != null) { Message msg = mHandlerFromMainActivity.obtainMessage(); msg.what = SearchListener.MSG_ANIMATION_DONE; mHandlerFromMainActivity.handleMessage(msg); } } }); if (mHandlerFromMainActivity != null) { Message msg = mHandlerFromMainActivity.obtainMessage(); msg.what = SearchListener.MSG_ANIMATION_PREPARE; mHandlerFromMainActivity.handleMessage(msg); } mUpDownLayout.startAnimation(downAnimation); } /** * 上移。 */ private void layoutUp() { mUpDownLayout.layout(mUpDownLayout.getLeft(), 0, mUpDownLayout.getRight(), mUpDownLayout.getHeight()); mUpDownLayout.setDrawingCacheEnabled(true); mUpDownLayout.buildDrawingCache(); } /** * 下移。 */ private void layoutDown() { int workSpacePosition = getWorkSpaceDownPosition(); mUpDownLayout.layout(mUpDownLayout.getLeft(), workSpacePosition, mUpDownLayout.getRight(), workSpacePosition + mUpDownLayout.getHeight()); mUpDownLayout.setDrawingCacheEnabled(false); } /** * 获取workspace下移后的(top)position。 * @return position. */ private int getWorkSpaceDownPosition() { //A部分的高度 int heightPartA = getResources().getDimensionPixelSize(R.dimen.browseraddview_height); return getHeight() - heightPartA; } /** * 打log用. * @param log log */ private void logCat(String log) { } /** * 处理动画的Handler. */ private Handler mHandler = new Handler() { public void handleMessage(Message msg) { if (mIsMove) { return; } switch (msg.what) { case MESSAGE_MOVING_UP: // top.setVisibility(View.GONE); if (!mIsUp) { mIsUp = true; moveUp(); } break; case MESSAGE_MOVING_DOWN: if (mIsUp) { mIsUp = false; // if(mHandler_fromActivity != null){ // mHandler_fromActivity.sendEmptyMessage(SearchListener.MSG_DING_MOVE_DOWN); // } moveDown(); } break; case MESSAGE_MOVING_CLICK: Object obj = msg.obj; Float t = (Float) obj; int dingLayoutHeight = getResources().getDimensionPixelSize(R.dimen.browseraddview_height); // t<ding_layout_height在ding_layout中点击才是有效的,在webview中点击是无效的 if (mIsUp && t < dingLayoutHeight) { moveDown(); mIsUp = false; } else if (t < dingLayoutHeight) { moveUp(); mIsUp = true; } break; default: if (mHandlerFromMainActivity != null) { mHandlerFromMainActivity.handleMessage(msg); } break; } } }; }
item的布局:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:ignore="PxUsage" android:layout_width="fill_parent" android:layout_height="fill_parent"> <ImageView android:id="@+id/ding_part_a_background" android:layout_width="fill_parent" android:layout_height="@dimen/browseraddview_height" android:background="@drawable/ding_rectangle" /> <View android:id="@+id/ding_topline1" android:layout_width="fill_parent" android:layout_height="1px" android:layout_alignParentTop="true" android:background="#E5000000" /> <View android:id="@+id/ding_topline2" android:layout_width="fill_parent" android:layout_height="1px" android:layout_below="@+id/ding_topline1" android:background="#19ffffff" /> <!-- 阿拉丁A部分显示4行文字 --> <TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/ding_topline2" android:layout_marginRight="50dip" android:layout_marginLeft="10dip" android:layout_marginTop="8dip" style="@style/DingTypeFaceStyle" android:textSize="18.6dip" android:textColor="#ffffff" android:text="" /> <TextView android:id="@+id/main_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/title" android:layout_marginLeft="10dip" android:layout_marginTop="0dip" style="@style/DingTypeFaceStyle" android:textSize="32dip" android:textColor="#ffffff" android:text="数据加载中..." /> <TextView android:id="@+id/description" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/main_content" android:layout_marginLeft="10dip" android:layout_marginTop="0dip" style="@style/DingTypeFaceStyle" android:textSize="13.3dip" android:textColor="#ffffff" android:text="" /> <TextView android:id="@+id/time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/description" android:layout_marginLeft="10dip" android:layout_marginTop="2dip" style="@style/DingTypeFaceStyle" android:textSize="10.6dip" android:textColor="#ffffff" android:text="" /> <!-- 阿拉丁A部分右侧的刷新按钮布局 --> <ImageView android:id="@+id/btn_refresh" android:src="@drawable/trans_ding_refresh" android:layout_height="38dip" android:layout_width="38dip" android:layout_alignParentRight="true" android:layout_marginTop="5dip" android:visibility="visible" /> <ProgressBar android:id="@+id/progressrefresh" android:layout_height="38dip" android:layout_width="38dip" android:layout_alignParentRight="true" android:layout_marginTop="5dip" android:visibility="invisible" android:indeterminateDrawable="@anim/webview_loading_progressbar_anim" /> <ImageView android:id="@+id/ding_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/btn_refresh" android:layout_alignParentRight="true" android:layout_marginRight="10dip" android:layout_marginTop="16dip" android:src="@drawable/ding_icon" /> <View android:id="@+id/ding_part_b_background" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_below="@+id/ding_part_a_background" android:background="@drawable/ding_rectangle" /> <TextView android:id="@+id/app_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_alignTop="@+id/ding_part_b_background" android:layout_marginTop="70dip" android:textSize="24sp" android:textColor="#7fffffff" android:text="@string/app_name" /> <TextView android:id="@+id/loading_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/app_name" android:layout_centerHorizontal="true" android:layout_marginTop="10dip" android:textSize="18sp" android:textColor="#7fffffff" android:text="正在加载,请稍候..." /> </RelativeLayout>