Android(安卓)仿今日头条频道管理(上)(GridView之间Item的移动和拖拽)
前言
经常逛今日头条、发现它的频道管理功能做的特别赞,交互体验非常好、如图:
它是2个gridview组成、2个gridview之间的Item是可以相互更换的、而且我的频道的Item是可以拖拽进行排序。仔细观察、今日头条有些细节做的的非常好,当一个gridview1的item移动到另一个gridview2时、gridview1的item不会立即消失、而是有一个没有内容的背景框、等item移动gridview2操作完毕才会消失、并且gridview2在gridview1的Item到达之前也有一个没有内容的背景框,等到达后覆盖。给用户一个很强的交互体验,很赞。而且在item拖拽移动排序时、拖拽的item会变大变色、效果很赞,体验极好。感兴趣的话可以下一个今日头条的客户端看看。当看到这么赞的交互体验,我就想看看是怎么实现的,这篇博客先讲2个Gridview之间item的移动,下一篇在带来gridview的拖拽排序。
实现思路
要实现2个gridview之间的Item相互移动:
1、首先我们获取我们点击的位置、处于gridview哪个位置
2、获取位置后、我们就能拿到这个Item的View,我们获取item绘制缓存的Bitmap对象。
3、将Bitmap设置的一个Imageview上,然后将这个ImageView放到一个容器中去进行移动操作,这样可能有人有疑问,为什么不直接把item放到容器中去呢,是因为item已经有自己的父容器gridview,所以我们new一个Imageview来代替item
4、然后我们将imageview移动到另一个gridview的最后一个位置。
5、最后刷新2个gridview的视图、就能实现我们所见的效果。
实现代码
主程序代码:
package com.test.drag;import android.graphics.Bitmap;import android.os.Bundle;import android.os.Handler;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.view.ViewGroup;import android.view.animation.Animation;import android.view.animation.AnimationSet;import android.view.animation.TranslateAnimation;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.GridView;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.TextView;import com.test.drag.view.MyGridView;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity implements OnItemClickListener { private MyGridView mUserGv, mOtherGv; private List mUserList = new ArrayList<>(); private List mOtherList = new ArrayList<>(); private OtherAdapter mUserAdapter, mOtherAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } public void initView() { mUserGv = (MyGridView) findViewById(R.id.userGridView); mOtherGv = (MyGridView) findViewById(R.id.otherGridView); mUserList.add("推荐"); mUserList.add("热点"); mUserList.add("上海"); mUserList.add("时尚"); mUserList.add("科技"); mUserList.add("体育"); mUserList.add("军事"); mUserList.add("财经"); mUserList.add("网络"); mOtherList.add("汽车"); mOtherList.add("房产"); mOtherList.add("社会"); mOtherList.add("情感"); mOtherList.add("女人"); mOtherList.add("旅游"); mOtherList.add("健康"); mOtherList.add("美女"); mOtherList.add("游戏"); mOtherList.add("数码"); mOtherList.add("娱乐"); mOtherList.add("探索"); mUserAdapter = new OtherAdapter(this, mUserList,true); mOtherAdapter = new OtherAdapter(this, mOtherList,false); mUserGv.setAdapter(mUserAdapter); mOtherGv.setAdapter(mOtherAdapter); mUserGv.setOnItemClickListener(this); mOtherGv.setOnItemClickListener(this); } /** *获取点击的Item的对应View, *因为点击的Item已经有了自己归属的父容器MyGridView,所有我们要是有一个ImageView来代替Item移动 * @param view * @return */ private ImageView getView(View view) { view.destroyDrawingCache(); view.setDrawingCacheEnabled(true); Bitmap cache = Bitmap.createBitmap(view.getDrawingCache()); view.setDrawingCacheEnabled(false); ImageView iv = new ImageView(this); iv.setImageBitmap(cache); return iv; } /** * 获取移动的VIEW,放入对应ViewGroup布局容器 * @param viewGroup * @param view * @param initLocation * @return */ private View getMoveView(ViewGroup viewGroup, View view, int[] initLocation) { int x = initLocation[0]; int y = initLocation[1]; viewGroup.addView(view); LinearLayout.LayoutParams mLayoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); mLayoutParams.leftMargin = x; mLayoutParams.topMargin = y; view.setLayoutParams(mLayoutParams); return view; } /** * 创建移动的ITEM对应的ViewGroup布局容器 * 用于存放我们移动的View */ private ViewGroup getMoveViewGroup() { //window中最顶层的view ViewGroup moveViewGroup = (ViewGroup) getWindow().getDecorView(); LinearLayout moveLinearLayout = new LinearLayout(this); moveLinearLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); moveViewGroup.addView(moveLinearLayout); return moveLinearLayout; } /** * 点击ITEM移动动画 * * @param moveView * @param startLocation * @param endLocation * @param moveChannel * @param clickGridView */ private void MoveAnim(View moveView, int[] startLocation, int[] endLocation, final String moveChannel, final GridView clickGridView, final boolean isUser) { int[] initLocation = new int[2]; //获取传递过来的VIEW的坐标 moveView.getLocationInWindow(initLocation); //得到要移动的VIEW,并放入对应的容器中 final ViewGroup moveViewGroup = getMoveViewGroup(); final View mMoveView = getMoveView(moveViewGroup, moveView, initLocation); //创建移动动画 TranslateAnimation moveAnimation = new TranslateAnimation( startLocation[0], endLocation[0], startLocation[1], endLocation[1]); moveAnimation.setDuration(300L);//动画时间 //动画配置 AnimationSet moveAnimationSet = new AnimationSet(true); moveAnimationSet.setFillAfter(false);//动画效果执行完毕后,View对象不保留在终止的位置 moveAnimationSet.addAnimation(moveAnimation); mMoveView.startAnimation(moveAnimationSet); moveAnimationSet.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { moveViewGroup.removeView(mMoveView); // 判断点击的是DragGrid还是OtherGridView if (isUser) { mOtherAdapter.setVisible(true); mOtherAdapter.notifyDataSetChanged(); mUserAdapter.remove(); } else { mUserAdapter.setVisible(true); mUserAdapter.notifyDataSetChanged(); mOtherAdapter.remove(); } } }); } @Override public void onItemClick(AdapterView<?> parent, View view, final int position, long id) { switch (parent.getId()) { case R.id.userGridView: //position为 0,1 的不可以进行任何操作 if (position != 0 && position != 1) { final ImageView moveImageView = getView(view); if (moveImageView != null) { TextView newTextView = (TextView) view.findViewById(R.id.text_item); final int[] startLocation = new int[2]; newTextView.getLocationInWindow(startLocation); final String channel = ((OtherAdapter) parent.getAdapter()).getItem(position);//获取点击的频道内容 mOtherAdapter.setVisible(false); //添加到最后一个 mOtherAdapter.addItem(channel); new Handler().postDelayed(new Runnable() { public void run() { try { int[] endLocation = new int[2]; //获取终点的坐标 mOtherGv.getChildAt(mOtherGv.getLastVisiblePosition()).getLocationInWindow(endLocation); MoveAnim(moveImageView, startLocation, endLocation, channel, mUserGv, true); mUserAdapter.setRemove(position); } catch (Exception localException) { } } }, 50L); } } break; case R.id.otherGridView: final ImageView moveImageView = getView(view); if (moveImageView != null) { TextView newTextView = (TextView) view.findViewById(R.id.text_item); final int[] startLocation = new int[2]; newTextView.getLocationInWindow(startLocation); final String channel = ((OtherAdapter) parent.getAdapter()).getItem(position); mUserAdapter.setVisible(false); //添加到最后一个 mUserAdapter.addItem(channel); new Handler().postDelayed(new Runnable() { public void run() { try { int[] endLocation = new int[2]; //获取终点的坐标 mUserGv.getChildAt(mUserGv.getLastVisiblePosition()).getLocationInWindow(endLocation); MoveAnim(moveImageView, startLocation, endLocation, channel, mOtherGv,false); mOtherAdapter.setRemove(position); } catch (Exception localException) { } } }, 50L); } break; default: break; } }}
适配器代码:
package com.test.drag;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.TextView;import java.util.List;/** * Created by fuweiwei on 2016/1/8. */public class OtherAdapter extends BaseAdapter { private Context context; public List channelList; private TextView item_text; /** 是否可见 在移动动画完毕之前不可见,动画完毕后可见*/ boolean isVisible = true; /** 要删除的position */ public int remove_position = -1; /** 是否是用户频道 */ private boolean isUser = false; public OtherAdapter(Context context, List channelList ,boolean isUser) { this.context = context; this.channelList = channelList; this.isUser = isUser; } @Override public int getCount() { return channelList == null ? 0 : channelList.size(); } @Override public String getItem(int position) { if (channelList != null && channelList.size() != 0) { return channelList.get(position); } return null; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { View view = LayoutInflater.from(context).inflate(R.layout.adapter_mygridview_item, null); item_text = (TextView) view.findViewById(R.id.text_item); String channel = getItem(position); item_text.setText(channel); if(isUser){ if ((position == 0) || (position == 1)){ item_text.setEnabled(false); } } if (!isVisible && (position == -1 + channelList.size())){ item_text.setText(""); item_text.setSelected(true); item_text.setEnabled(true); } if(remove_position == position){ item_text.setText(""); } return view; } /** 获取频道列表 */ public List getChannnelLst() { return channelList; } /** 添加频道列表 */ public void addItem(String channel) { channelList.add(channel); notifyDataSetChanged(); } /** 设置删除的position */ public void setRemove(int position) { remove_position = position; notifyDataSetChanged(); // notifyDataSetChanged(); } /** 删除频道列表 */ public void remove() { channelList.remove(remove_position); remove_position = -1; notifyDataSetChanged(); } /** 设置频道列表 */ public void setListDate(List list) { channelList = list; } /** 获取是否可见 */ public boolean isVisible() { return isVisible; } /** 设置是否可见 */ public void setVisible(boolean visible) { isVisible = visible; }}
至于之前说的很赞的交互体验是怎么实现的:
1、在点击gridview1的item时(也就是Imageview移动动画开始时),我们将所点击的内容置为空,
2、在gridview1点击的同时我们在gridview2的最后面加一个我们刚点击的item,但内容显示也是空
3、当imageview移动动画结束后,我们将gridview1所点击的item移除、重新刷新界面,而gridview2我们只要设置最后一个内容显示、刷新视图。
这样就能实现今天头条一样超赞的交互体验。
其它代码:
package com.test.drag.view;import android.content.Context;import android.util.AttributeSet;import android.widget.GridView;/** * Created by fuweiwei on 2016/1/8. */public class MyGridView extends GridView { public MyGridView(Context paramContext, AttributeSet paramAttributeSet) { super(paramContext, paramAttributeSet); } @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); }}
布局:
<?xml version="1.0" encoding="utf-8"?>
实现的效果如下:
是不是效果一模一样,很喜欢这样交互体验,这里只介绍了gridview之间item的移动,至于gridview的Item的拖拽比较复杂、会在下一篇博客记录。
源码下载
更多相关文章
- 一次偶然机遇找到一个不错的关于Android实现加载gif动画的实例,写
- Android(安卓)OpenGLES2.0(十三)——流畅的播放逐帧动画
- 我的工具太少了之Android无限轮播图片,最后一张过度动画很重要
- Android(安卓)使用动画效果后的控件位置处理 类似系统通知栏下拉
- Android(安卓)PowerImageView实现,可以播放动画的强大ImageView
- Android里的动画(补间动画,帧动画,属性动画)
- [置顶] 关于代码家(干货集中营)共享知识点汇总系列——Android
- Android共享动画兼容实现
- Android(安卓)动画之ScaleAnimation应用详解