Android 仿網(wǎng)易新聞客戶端分類排序功能
先來(lái)看看網(wǎng)易新聞客戶端以及自己實(shí)現(xiàn)的效果圖,效果當(dāng)然還是網(wǎng)易的好
gridviewsort.gif
如何實(shí)現(xiàn)拖拽一個(gè)Item
用WindowManager添加一個(gè)ImageView,并且將這個(gè)ImageView的顯示圖片設(shè)置成被拖拽item的截圖,截圖可以通過(guò)View的getDrawingCache獲得。拖拽的時(shí)候,隱藏原始的item。處理觸摸事件的ActionMove,調(diào)整ImageView的位置,跟隨手指移動(dòng)。在ActionUp的時(shí)候removeView
GridView
@Override public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) { // 至少有兩個(gè)item的時(shí)候,才有排序 if (getChildCount() >= 2) { mView = view; // 在調(diào)用getDrawingCache必須先調(diào)用 view.setDrawingCacheEnabled(true); // 獲取截圖并設(shè)置 Bitmap bitmap = view.getDrawingCache(); mDragItemView.setImageBitmap(bitmap); // 設(shè)置拖拽的imageview的params mDragItemLayoutParams.gravity = Gravity.TOP | Gravity.LEFT; mDragItemLayoutParams.width = bitmap.getWidth(); mDragItemLayoutParams.height = bitmap.getHeight(); mDragItemLayoutParams.x = (mDownX - mDragItemLayoutParams.width / 2); mDragItemLayoutParams.y = (mDownY - mDragItemLayoutParams.height / 2); // 設(shè)置拖拽imageview的中心位于長(zhǎng)按點(diǎn)擊點(diǎn) mDragItemLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE //不接受按鍵事件 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE // 不接收觸摸事件 | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON // 保持常亮 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; // place the window within the entire screen, ignoring decorations around the border (such as the status bar) mDragItemLayoutParams.format = PixelFormat.TRANSLUCENT; mDragItemLayoutParams.windowAnimations = 0; // 往WindowManager中添加拖拽的View mWindowManager.addView(mDragItemView, mDragItemLayoutParams); ((GridViewSortAdapter) getAdapter()).init(); ((GridViewSortAdapter) getAdapter()).hideView(i); Log.d(TAG, "long click = " + i); mDragStarted = true; } return true; } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction() & ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: mDownX = (int) ev.getRawX(); mDownY = (int) ev.getRawY(); break; case MotionEvent.ACTION_MOVE: if (mDragStarted) { // 保持中心 mDragItemLayoutParams.x = (int) (ev.getRawX() - mDragItemView.getWidth() / 2); mDragItemLayoutParams.y = (int) (ev.getRawY() - mDragItemView.getHeight() / 2); // 更新params mWindowManager.updateViewLayout(mDragItemView, mDragItemLayoutParams); // ...... } break; case MotionEvent.ACTION_UP: // ...... break; } return super.onTouchEvent(ev); }
如何實(shí)現(xiàn)隱藏拖拽的Item
在開始拖拽的時(shí)候,把隱藏的item的position告訴Adapter,調(diào)用Adapter的notifyDataSetChanged刷新數(shù)據(jù),在getView方法中判斷當(dāng)前的構(gòu)建的item的position是不是需要隱藏的position是的話就設(shè)置view為inVisible
GridView
@Override public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) { // ...... ((GridViewSortAdapter) getAdapter()).hideView(i); // ...... } GridViewSortAdapter public void hideView(int item) { // ...... mStartHideItemPosition = item; notifyDataSetChanged(); } private int mStartHideItemPosition = AdapterView.INVALID_POSITION; @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { convertView = LayoutInflater.from(mContext).inflate(R.layout.view_item_grid_view_sort, null); holder = new ViewHolder(); holder.title = (TextView) convertView.findViewById(R.id.view_item_grid_view_sort_title); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.title.setText(mTypeTitle.get(position)); if (mStartHideItemPosition == position) { convertView.setVisibility(View.INVISIBLE); } else { convertView.setVisibility(View.VISIBLE); } return convertView; }
如何知道當(dāng)前拖拽到哪一個(gè)item之上
要想在拖拽到其他item上面時(shí)互換位置,那必須得知道當(dāng)前拖拽到了哪一個(gè)item之上。GrideView提供了一個(gè)方法叫pointToPosition,可以在處理觸摸事件的ACTION_MOVE時(shí),獲取手指觸摸的x,y來(lái)得到當(dāng)前拖拽到item之上的position。這里需要注意的一點(diǎn)是,在拖拽的過(guò)程,同一個(gè)item的position是不會(huì)變的,除非調(diào)用了Adapter的notifyDataSetChanged,position才會(huì)重新計(jì)算。比如position為2的item,在拖拽的過(guò)程無(wú)論怎么動(dòng)畫移動(dòng)位置,他的position都是2,知道一次拖拽結(jié)束,ActionUp的時(shí)候,會(huì)調(diào)用notifyDataSetChanged
GridView
@Override public boolean onTouchEvent(MotionEvent ev) { case MotionEvent.ACTION_MOVE: if (mDragStarted) { // ...... int position = pointToPosition((int) ev.getX(), (int) ev.getY()); // ...... } break; }
如何實(shí)現(xiàn)動(dòng)畫
一個(gè)item需要水平以及垂直需要移動(dòng)的距離可以事先先計(jì)算出來(lái),其實(shí)水平距離不管怎么樣一定會(huì)是GridView一個(gè)單元格的寬度加上水平間距,垂直距離無(wú)論如何都是一個(gè)單元格的高度加上垂直距離,寬度非常好取,高度的話,這里默認(rèn)item 的高度和單元格的高度相同。
GridViewSortAdapter
View view = mGridView.getChildAt(0); mTranslateX = view.getWidth() + mHorizontalSpace; mTranslateY = view.getHeight() + mVerticalSpace;
當(dāng)拖拽到其他item之上時(shí),開始動(dòng)畫
SortGridView
if (position != AdapterView.INVALID_POSITION && !((GridViewSortAdapter) getAdapter()).isInAnimation()) { Log.d(TAG, "position = " + position); ((GridViewSortAdapter) getAdapter()).swap(position); } GridSortAdapter public void swap(int position) { mAnimatorSetList.clear(); int r_p = mPositionList.indexOf(position); Log.d(TAG, "r_p = " + r_p); if (mCurrentHideItemPosition < r_p) { for (int i = mCurrentHideItemPosition + 1; i <= r_p; i++) { View v = mGridView.getChildAt(mPositionList.get(i)); if (i % mColsNum == 0 && i > 0) { startMoveAnimation(v, v.getTranslationX() + mTranslateX * (mColsNum - 1), v.getTranslationY() - mTranslateY); } else { startMoveAnimation(v, v.getTranslationX() - mTranslateX, 0); } } } else if (mCurrentHideItemPosition > r_p) { for (int i = r_p; i < mCurrentHideItemPosition; i++) { View v = mGridView.getChildAt(mPositionList.get(i)); if ((i + 1) % mColsNum == 0) { startMoveAnimation(v, v.getTranslationX() - mTranslateX * (mColsNum - 1), v.getTranslationY() + mTranslateY); } else { startMoveAnimation(v, v.getTranslationX() + mTranslateX, 0); } } } resetPositionList(); int value = mPositionList.get(mStartHideItemPosition); if (mStartHideItemPosition < r_p) { mPositionList.add(r_p + 1, value); mPositionList.remove(mStartHideItemPosition); } else if (mStartHideItemPosition > r_p) { mPositionList.add(r_p, value); mPositionList.remove(mStartHideItemPosition + 1); } mCurrentHideItemPosition = r_p; } public boolean isInAnimation() { return mInAnimation; } private void resetPositionList() { mPositionList.clear(); for (int i = 0; i < mGridView.getChildCount(); i++) { mPositionList.add(i); } } private void startMoveAnimation(View myView, float x, float y) { AnimatorSet set = new AnimatorSet(); set.playTogether( ObjectAnimator.ofFloat(myView, "translationX", myView.getTranslationX(), x), ObjectAnimator.ofFloat(myView, "translationY", myView.getTranslationY(), y) ); set.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { mInAnimation = true; } @Override public void onAnimationEnd(Animator animator) { mInAnimation = false; } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); mAnimatorSetList.add(set); set.setDuration(150).start(); }
這里我主要解釋一下代碼中 mPositionList這個(gè)列表的作用,之前說(shuō)過(guò)一次拖拽的時(shí)候,item的position是不會(huì)變化的。
假設(shè)有一組數(shù)據(jù)
a b c d
e f g h
i j k l
此時(shí)mPositionList的內(nèi)容就是 0 1 2 3 4 5 6 7 8 9 10 11 12
現(xiàn)在將c拖拽到g上,拖拽完成之后的數(shù)據(jù)應(yīng)該是,未釋放手指
a b d e
f g c h
i j k l
此時(shí)mPositionList的內(nèi)容就是 0 1 2 4 5 6 7 3 8 9 10 11 12
緊接著,繼續(xù)拖拽c到e上,你會(huì)發(fā)現(xiàn)調(diào)用pointToPosition方法得到的position是5,但是e現(xiàn)在的索引是4
因此你只需要調(diào)用
mPositionList.indexOf(pointToPosition(x,y))
就能得到真實(shí)的item的position
其他
如果把GridView的列數(shù)變成1那么似曾相識(shí)啊
gridviewex.gif
源碼地址
以上所述是小編給大家介紹的Android 仿網(wǎng)易新聞客戶端分類排序功能,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- Android項(xiàng)目實(shí)戰(zhàn)之仿網(wǎng)易新聞的頁(yè)面(RecyclerView )
- Android實(shí)現(xiàn)仿網(wǎng)易新聞的頂部導(dǎo)航指示器
- Android實(shí)現(xiàn)仿網(wǎng)易新聞主界面設(shè)計(jì)
- Android實(shí)現(xiàn)網(wǎng)易新聞客戶端首頁(yè)效果
- Android實(shí)現(xiàn)類似網(wǎng)易新聞選項(xiàng)卡動(dòng)態(tài)滑動(dòng)效果
- Android模擬實(shí)現(xiàn)網(wǎng)易新聞客戶端
- Android實(shí)現(xiàn)網(wǎng)易新聞客戶端側(cè)滑菜單(1)
- Android組件DrawerLayout仿網(wǎng)易新聞v4.4側(cè)滑菜單
- Android實(shí)現(xiàn)網(wǎng)易新聞客戶端側(cè)滑菜單(2)
- Android仿網(wǎng)易新聞圖片詳情下滑隱藏效果示例代碼
相關(guān)文章
使用RecyclerView添加Header和Footer的方法
RecyclerView雖然作為L(zhǎng)istView的替代者有著較好的性能提升,但是ListView的一些常用功能卻沒有提供,比如我們平時(shí)會(huì)經(jīng)常用到的addHeaderView,addFooterView,既然RecyclerView沒有提供這個(gè)方法,我們應(yīng)該如何為列表添加頭部和底部呢,接下來(lái)通過(guò)本文給大家介紹2016-03-03Android開發(fā)技巧之在a標(biāo)簽或TextView控件中單擊鏈接彈出Activity(自定義動(dòng)作)
a標(biāo)簽以及TextView自動(dòng)識(shí)別的特殊文本(網(wǎng)址、電話號(hào)、Email等),這些都可以通過(guò)單擊來(lái)觸發(fā)不同的動(dòng)作;但如果讀者想在單擊鏈接時(shí)執(zhí)行任意自定義的動(dòng)作,那么將要介紹的一定是你想要的了2013-01-01Android中LayoutInflater.inflater()的正確打開方式
這篇文章主要給大家介紹了關(guān)于Android中LayoutInflater.inflater()的正確打開方式,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-12-12Android中CountDownTimer 實(shí)現(xiàn)倒計(jì)時(shí)功能
本篇文章主要介紹了Android中CountDownTimer 實(shí)現(xiàn)倒計(jì)時(shí)功能,CountDownTimer 是android 自帶的一個(gè)倒計(jì)時(shí)類,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05Android studio設(shè)計(jì)簡(jiǎn)易計(jì)算器
這篇文章主要為大家詳細(xì)介紹了Android studio設(shè)計(jì)簡(jiǎn)易計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04Android 6.0以上權(quán)限拒絕打開權(quán)限設(shè)置界面的解決方法
今天小編就為大家分享一篇Android 6.0以上權(quán)限拒絕打開權(quán)限設(shè)置界面的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-07-07Android Studio 升級(jí)到3.0后輸入法中文狀態(tài)下無(wú)法選詞的終極解決方案
這篇文章主要介紹了 AndroidStudio 升級(jí)到3.0后輸入法中文狀態(tài)下無(wú)法選詞的解決方案,需要的朋友可以參考下2017-11-11