今日头条 频道管理(删除、添加、拖动)
2014-10-13 11:54
716 查看
频道管理共分为两部分:
一、频道的删除和添加
二、频道的拖动
一、频道的删除和添加
利用监听来进行动画的执行。
动画:以item本身创建Bitmap, 然后放入顶层ViewGroup 执行动画并监听Animation(动画为TranslateAnimation 移动)
代码:
二、频道的拖动
思路:重写GridView
通过setOnItemLongClickListener()长时间按住触动 创建窗口 进行拖动
1、创建移动窗口
利用WindowManager WindowManager.LayoutParams 进行窗口创建
2、拖动频道移动
通过TranslateAnimation进行频道移动
代码下载:点击下载
一、频道的删除和添加
二、频道的拖动
一、频道的删除和添加
利用监听来进行动画的执行。
动画:以item本身创建Bitmap, 然后放入顶层ViewGroup 执行动画并监听Animation(动画为TranslateAnimation 移动)
代码:
public class MyActivity extends Activity{ private DragGrid userGridView; private OtherGridView otherGridView; private DragGridAdapter adapter; private ArrayList<String> data1 = null; private ArrayList<String> data2 = null; private OtherAdapter otherAdapter; //1、防止报java.lang.ArrayIndexOutOfBoundsException 异常,用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引 //主要是当连续点击那个还没有移除item的时候, adapter.remove回报这个错 private boolean isMove = false; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.main); initView(); initListener(); } private void initView(){ userGridView = (DragGrid)findViewById(R.id.userGridView); otherGridView = (OtherGridView) findViewById(R.id.otherGridView); data1 = new ArrayList<String>(); data2 = new ArrayList<String>(); String[] s = new String[]{"如果当时", "多余的解释", "有何不可", "坏孩子", "清明雨上", "城府", "认错", "内线", "星座书上"}; for(int i = 0;i < s.length;i ++){ data1.add(s[i]); } String[] ss = new String[]{"等到烟火清凉", "山水之间", "七夕", "有桃花", "惊鸿一面", "隐隐约约", "宇宙之大", "梧桐灯", "弹指一挥间", "胡萝卜须", "幻听", "对话老师", "伴虎", "闺蜜", "装糊涂", "play with style", "心疼你的过去", "全球变冷", "亲情式的爱情"}; for(int i = 0;i < ss.length;i ++){ data2.add(ss[i]); } adapter = new DragGridAdapter(this, data1); otherAdapter = new OtherAdapter(this, data2); userGridView.setAdapter(adapter); otherGridView.setAdapter(otherAdapter); } private void initListener(){ userGridView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, final int position, long id) { // TODO Auto-generated method stub if(isMove){ return; } if(position > 1){ final ImageView moveImage = getView(view); if(moveImage != null){ TextView nowTextView = (TextView) view.findViewById(R.id.text_item); final int[] startCoord = new int[2]; //getLocationInWindow:获取在整个窗口内的绝对坐标 //将view的左上角坐标存入数组中.此坐标是相对当前activity而言. nowTextView.getLocationInWindow(startCoord); //setVisible():显示动画要移动的位置 otherAdapter.setVisible(false); otherAdapter.add(data1.get(position)); //postDelayed():延时50L之后执行 new Handler().postDelayed(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub int[] endCoord = new int[2]; otherGridView.getChildAt(otherGridView.getLastVisiblePosition()).getLocationInWindow(endCoord); adapter.setRemove(position); moveAnim(moveImage, startCoord, endCoord, userGridView); } }, 50L); } } } }); otherGridView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, final int position, long id) { // TODO Auto-generated method stub if (isMove) { return; } if (position > 1) { final ImageView moveImage = getView(view); if (moveImage != null) { TextView nowTextView = (TextView) view .findViewById(R.id.text_item); final int[] startCoord = new int[2]; nowTextView.getLocationInWindow(startCoord); adapter.setVisible(false); adapter.add(data2.get(position)); new Handler().postDelayed(new Runnable() { @Override public void run() { // TODO Auto-generated method stub int[] endCoord = new int[2]; userGridView.getChildAt(userGridView.getLastVisiblePosition()).getLocationInWindow(endCoord); otherAdapter.setRemove(position); moveAnim(moveImage, startCoord, endCoord, otherGridView); } }, 50L); } } } }); } /* 移动动画*/ private void moveAnim(View view, int[] start, int[] end, final GridView clickGridView){ //getViewGroup、getMoveView :因为Bitmap 不支持Animation动画 所以用控件包含Bitmap 用控件来展示动画 final ViewGroup viewGroup = getViewGroup(); final View moveView = getMoveView(viewGroup, view); //创建移动动画 TranslateAnimation moveAnimation = new TranslateAnimation(start[0], end[0], start[1], end[1]); moveAnimation.setDuration(300L); AnimationSet moveAnimationSet = new AnimationSet(true); moveAnimationSet.setFillAfter(false);//动画效果执行完毕后,View对象不保留在终止的位置 moveAnimationSet.addAnimation(moveAnimation); moveView.startAnimation(moveAnimationSet); moveAnimationSet.setAnimationListener(new AnimationListener() { public void onAnimationStart(Animation animation) { // TODO Auto-generated method stub isMove = true; } public void onAnimationRepeat(Animation animation) { // TODO Auto-generated method stub } public void onAnimationEnd(Animation animation) { // TODO Auto-generated method stub //删除添加的控件 viewGroup.removeView(moveView); if(clickGridView instanceof DragGrid){ otherAdapter.setVisible(true); otherAdapter.notifyDataSetChanged(); adapter.remove(); }else { adapter.setVisible(true); adapter.notifyDataSetChanged(); otherAdapter.remove(); } isMove = false; } }); } /* 把Bitmap添加进ViewGroup*/ private View getMoveView(ViewGroup group, View view){ group.addView(view); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); view.setLayoutParams(params); return view; } /* 获取ViewGroup */ private ViewGroup getViewGroup(){ //getDecorView():decorView是window中的最顶层view ViewGroup group = (ViewGroup)getWindow().getDecorView(); LinearLayout moveLinearLayout = new LinearLayout(this); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); moveLinearLayout.setLayoutParams(params); group.addView(moveLinearLayout); return moveLinearLayout; } /* 用来获取绘制的图片*/ 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; } }
二、频道的拖动
思路:重写GridView
通过setOnItemLongClickListener()长时间按住触动 创建窗口 进行拖动
1、创建移动窗口
利用WindowManager WindowManager.LayoutParams 进行窗口创建
2、拖动频道移动
通过TranslateAnimation进行频道移动
public class DragGrid extends GridView { /** 每个ITEM之间的水平间距 */ private int mHorizontalSpacing = 15; /** 每个ITEM之间的竖直间距 */ private int mVerticalSpacing = 15; /** item高 */ private int itemHeight; /** item宽 */ private int itemWidth; /** 屏幕上的X */ private int win_view_x; /** 屏幕上的Y*/ private int win_view_y; /** window属性*/ private WindowManager.LayoutParams windowParams = null; /** WindowManager管理器 */ private WindowManager windowManager = null; /** 拖动的时候放大的倍数 */ private double dragScale = 1.2D; /** 拖动的时候对应ITEM的VIEW */ private View dragImageView = null; /** 点击时候的X位置 */ public int downX; /** 点击时候的Y位置 */ public int downY; /** 点击时候对应整个界面的X位置 */ public int windowX; /** 点击时候对应整个界面的Y位置 */ public int windowY; /** 一行的ITEM数量*/ private int nColumns = 4; /* 移动时候最后个动画的ID */ private String LastAnimationID; //长按开始位置(第一次) private int startPosition; //长按的位置 private int dragPosition; //是否移动 private boolean isMove = false; //移动的次数 private int moveNum = 0; public DragGrid(Context context) { super(context); init(context); } public DragGrid(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } public DragGrid(Context context, AttributeSet attrs) { super(context, attrs); init(context); } /** 在ScrollView内,所以要进行计算高度 */ @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); } private void init(Context context){ //将布局文件中设置的间距dip转为px mHorizontalSpacing = DataTools.dip2px(context, mHorizontalSpacing); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // TODO Auto-generated method stub if (ev.getAction() == MotionEvent.ACTION_DOWN) { windowX = (int) ev.getX(); windowY = (int) ev.getY(); setOnItemClickListener(ev); } return super.onInterceptTouchEvent(ev); } /* 有一种方法可以阻止父层的View截获touch事件,就是调用getParent().requestDisallowInterceptTouchEvent(true);方法。 一旦底层View收到touch的action后调用这个方法那么父层View就不会再调用onInterceptTouchEvent了,也无法截获以后的action。 */ /* * requestDisallowInterceptTouchEvent():这个方法就是告诉父层不需要调用onInterceptTouchEvent()方法 * * 在响应touch事件时,会先调用父控件onInterceptTouchEvent()方法,然后在往下传递 * 详细请看博客 */ @Override public boolean onTouchEvent(MotionEvent ev) { // TODO Auto-generated method stub int x = (int) ev.getX(); int y = (int) ev.getY(); if (dragImageView != null && dragPosition != AdapterView.INVALID_POSITION) { int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: windowX = (int) ev.getX(); windowY = (int) ev.getY(); break; case MotionEvent.ACTION_MOVE: onDrag((int) ev.getRawX(), (int) ev.getRawY()); if(!isMove){ onMove(x, y); } break; case MotionEvent.ACTION_UP: stopDrag(); onDrop(x, y); requestDisallowInterceptTouchEvent(false); break; default: break; } } return super.onTouchEvent(ev); } /* 显示拖动之后位置上的内容 */ private void onDrop(int x, int y){ DragGridAdapter mDragGridAdapter = (DragGridAdapter) getAdapter(); mDragGridAdapter.setShowDropItem(true); mDragGridAdapter.notifyDataSetChanged(); } /* 窗口跟随手指进行移动 */ private void onDrag(int rawX, int rawY){ if(dragImageView != null){ windowParams.alpha = 0.6f; windowParams.x = rawX - win_view_x; windowParams.y = rawY - win_view_y; windowManager.updateViewLayout(dragImageView, windowParams); } } private void setOnItemClickListener(final MotionEvent ev){ setOnItemLongClickListener(new OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { // TODO Auto-generated method stub startPosition = position; dragPosition = position; if(startPosition <= 1){ return false; } //getChildAt() 获取的是Grid 中的item ViewGroup dragViewGroup = (ViewGroup) getChildAt(position); TextView dragTextView = (TextView) dragViewGroup.findViewById(R.id.text_item); //setSelected:改变视图的选中状态。视图有选中和未选中两个状态。注意,选择状态不同于焦点。 //典型的选中的视图是象 ListView 和 GridView 这样的 AdapterView 中显示的 内容;选中的内容会显示为高亮 dragTextView.setSelected(true); dragTextView.setEnabled(false); itemHeight = dragViewGroup.getHeight(); itemWidth = dragViewGroup.getWidth(); // System.out.println("dragViewGroup.getHeight()->" + dragViewGroup.getHeight()); // System.out.println("dragViewGroup.getWidth()->" + dragViewGroup.getWidth()); // System.out.println("dragTextView.getHeight()->" + dragTextView.getHeight()); // System.out.println("dragTextView.getWidth()->" + dragTextView.getWidth()); if(dragPosition != AdapterView.INVALID_POSITION){ //现在获取的ev.getX,ev.getY 与 windowX 、windowY 怎么会不一样那 尤其是Y坐标会差距那么大 //getLeft, getRight, getTop, getBottom是相对其父视图的位置 /*event.getRowX():触摸点相对于屏幕原点的x坐标 *event.getX(): 触摸点相对于其所在组件原点的x坐标 */ win_view_x = windowX - dragViewGroup.getLeft(); win_view_y = windowY - dragViewGroup.getTop(); /* * 在Android中自有获取view中的cache内容,然后将内容转换成bitmap,方法名是:getDrawingCache(),返回结果为Bitmap * destroyDrawingCache():用来销毁旧的cache * setDrawingCacheEnabled():用来把cache开启,不然没有办法获取cache * getDrawingCache():用来获取view中的cache内容,并把获取的内容转换成Bitmap */ dragViewGroup.destroyDrawingCache(); dragViewGroup.setDrawingCacheEnabled(true); Bitmap dragBitmap = Bitmap.createBitmap(dragViewGroup.getDrawingCache()); dragViewGroup.setDrawingCacheEnabled(false); startDrag(dragBitmap, (int)ev.getRawX(), (int)ev.getRawY()); hideDropItem(); // dragViewGroup.setVisibility(View.INVISIBLE); dragTextView.setBackgroundResource(R.drawable.diji2); dragTextView.setText(""); isMove = false; //阻止父层的View截获touch事件 requestDisallowInterceptTouchEvent(true); return true; } return false; } }); } /** 隐藏 放下 的ITEM*/ private void hideDropItem() { ((DragGridAdapter) getAdapter()).setShowDropItem(false); } //创建悬浮窗口 private void startDrag(Bitmap bitmap, int rawX, int rawY){ stopDrag(); windowParams = new WindowManager.LayoutParams(); windowParams.gravity = Gravity.TOP | Gravity.LEFT; /* * windowParams.x * 如果忽略gravity属性,那么它表示窗口的绝对X位置。 * 什么是gravity属性呢?简单地说,就是窗口如何停靠。 * 当设置了 Gravity.LEFT 或 Gravity.RIGHT 之后,x值就表示到特定边的距离。 */ windowParams.x = rawX - win_view_x; /* * windowParams.y * 如果忽略gravity属性,那么它表示窗口的绝对Y位置。 * 当设置了Gravity.TOP 或 Gravity.BOTTOM 之后,y值就表示到特定边的距离。 */ windowParams.y = rawY - win_view_y; windowParams.width = (int) (dragScale * bitmap.getWidth()); windowParams.height = (int) (dragScale * bitmap.getHeight()); /* * FLAG_NOT_FOCUSABLE:不能获得按键输入焦点,所以不能向它发送按键或按钮事件 * FLAG_NOT_TOUCHABLE:不接受触摸屏事件。 * FLAG_KEEP_SCREEN_ON: 当此窗口为用户可见时,保持设备常开,并保持亮度不变 * FLAG_LAYOUT_IN_SCREEN:窗口占满整个屏幕,忽略周围的装饰边框(例如状态栏)。此窗口需考虑到装饰边框的内容 */ this.windowParams.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; // this.windowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE // | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE // | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON // | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; //期望的位图格式。默认为不透明.参考android.graphics.PixelFormat。 //还不是很清楚 windowParams.format = PixelFormat.TRANSLUCENT; //窗口所使用的动画设置。它必须是一个系统资源而不是应用程序资源,因为窗口管理器不能访问应用程序。 // windowParams.windowAnimations = 0; ImageView iv = new ImageView(getContext()); iv.setImageBitmap(bitmap); windowManager = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE); windowManager.addView(iv, windowParams); /* WindowManager是Android中一个重要的服务(Service )。WindowManager Service 是全局的,是唯一的。 * 它将用户的操作,翻译成为指令,发送给呈现在界面上的各个Window。Activity会将顶级的控件注册到 Window Manager 中, * 当用户真是触碰屏幕或键盘的时候,Window Manager就会通知到,而当控件有一些请求产生,也会经由ViewParent送回到Window Manager中。 * 从而完成整个通信流程。整个Android的窗口机制是基于一个叫做 WindowManager,这个接口可以添加view到屏幕,也可以从屏幕删除view。 * 它面向的对象一端是屏幕,另一端就是View,通过WindowManager的 addView方法创建View,这样产生出来的View根据 * WindowManager.LayoutParams属性不同,效果也就不同了。比如创建 系统顶级窗口,实现悬浮窗口效果! * WindowManager的方法很简单,基本用到的就三addView,removeView,updateViewLayout。接口, * 而WindowManager.LayoutParams的属性就多了,非常丰富,具体请查后面介绍 */ dragImageView = iv; } //删除多余窗口 private void stopDrag(){ if(dragImageView != null){ windowManager.removeView(dragImageView); dragImageView = null; } } //得到动画 private Animation getMoveAnimation(float x, float y){ //Animation.RELATIVE_TO_SELF:相对自己的百分比 //view.width * (百分比 + 1) //view.height * (百分比 + 1) TranslateAnimation translateAnimation = new TranslateAnimation( Animation.RELATIVE_TO_SELF, 0.0F, Animation.RELATIVE_TO_SELF,x, Animation.RELATIVE_TO_SELF, 0.0F, Animation.RELATIVE_TO_SELF, y);// 当前位置移动到指定位置 //请看博客 // translateAnimation.setFillAfter(true); //延迟 /* * public void setDuration (long durationMillis) * 参数durationMillis为动画的持续时间,单位为毫秒(ms) */ translateAnimation.setDuration(300L); return translateAnimation; } private void onMove(int x, int y){ final int belowPosition = pointToPosition(x, y); if(belowPosition <= 1){ return; } if(belowPosition == -1 || belowPosition == startPosition){ return; } if(dragPosition != startPosition){ dragPosition = startPosition; } moveNum = belowPosition - dragPosition; if(moveNum == 0){ return; } ViewGroup group = (ViewGroup) getChildAt(dragPosition); group.setVisibility(View.INVISIBLE); float to_x = 1;// 当前下方positon float to_y;// 当前下方右边positon //没有理解 //x_vlaue移动的距离百分比(相对于自己长度的百分比) float x_value = ((float) mHorizontalSpacing / (float) itemWidth) + 1.0f; //y_vlaue移动的距离百分比(相对于自己宽度的百分比) float y_value = ((float) mVerticalSpacing / (float) itemHeight) + 1.0f; int length = Math.abs(moveNum); int holdPosition; for(int i = 0;i < length;i ++){ if(moveNum > 0){ holdPosition = dragPosition + i + 1; if(holdPosition/nColumns == dragPosition/nColumns){ to_x = -x_value; to_y = 0; }else if(holdPosition % 4 == 0){ to_x = 3 * x_value; to_y = -y_value; }else{ to_x = -x_value; to_y = 0; } } else{ holdPosition = dragPosition - i - 1; if(holdPosition/nColumns == dragPosition/nColumns){ to_x = x_value; to_y = 0; }else if(holdPosition % 4 == 3){ to_x = - 3 * x_value; to_y = y_value; }else{ to_x = x_value; to_y = 0; } } ViewGroup moveViewGroup = (ViewGroup)getChildAt(holdPosition); Animation moveAnimation = getMoveAnimation(to_x, to_y); moveViewGroup.startAnimation(moveAnimation); if (holdPosition == belowPosition) { LastAnimationID = moveAnimation.toString(); } moveAnimation.setAnimationListener(new AnimationListener() { @Override public void onAnimationStart(Animation animation) { // TODO Auto-generated method stub isMove = true; } @Override public void onAnimationRepeat(Animation animation) { // TODO Auto-generated method stub } @Override public void onAnimationEnd(Animation animation) { // TODO Auto-generated method stub if (animation.toString().equalsIgnoreCase(LastAnimationID)) { DragGridAdapter mDragAdapter = (DragGridAdapter) getAdapter(); mDragAdapter.exchange(startPosition,belowPosition); startPosition = belowPosition; dragPosition = belowPosition; isMove = false; } } }); } } }
代码下载:点击下载
相关文章推荐
- Android 高仿 频道管理----网易、今日头条、腾讯视频 (可以拖动的GridView)附源码DEMO
- Android 高仿 频道管理----网易、今日头条、腾讯视频 (可以拖动的GridView)附源码DEMO
- RecyclerView之ItemTouchHelper仿今日头条频道管理拖动
- Android 高仿 频道管理----网易、今日头条、腾讯视频 (可以拖动的GridView)附源码DEMO
- Android 高仿 频道管理----网易、今日头条、腾讯视频 (可以拖动的GridView)附源码DEMO
- Android 高仿 频道管理----网易、今日头条、腾讯视频 (可以拖动的GridView)附源码DEMO
- Android 高仿 频道管理----网易、今日头条、腾讯视频 (可以拖动的GridView)附源码DEMO
- Android 高仿 频道管理----网易、今日头条、腾讯视频 (可以拖动的GridView)附源码DEMO
- Android 高仿 频道管理----网易、今日头条、腾讯视频 (可以拖动的GridView)附源码DEMO
- Android 高仿 频道管理----网易、今日头条、腾讯视频 (能够拖动的GridView)附源代码DEMO
- Android 高仿 频道管理----网易、今日头条、腾讯视频 (可以拖动的GridView)附源码DEMO
- Android 高仿 频道管理----网易、今日头条、腾讯视频 (可以拖动的GridView)附源码DEMO
- Android 高仿 频道管理----网易、今日头条、腾讯视频 (可以拖动的GridView)附源码DEMO
- Android 高仿 频道管理----网易、今日头条、腾讯视频 (可以拖动的GridView)附源码DEMO
- Android 高仿 频道管理----网易、今日头条、腾讯视频 (可以拖动的GridView)附源码DEMO
- Android 高仿 频道管理----网易、今日头条、腾讯视频 (可以拖动的GridView)附源码DEMO
- Android 高仿 频道管理----网易、今日头条、腾讯视频 (可以拖动的GridView)附源码DEMO
- Android 高仿 频道管理----网易、今日头条、腾讯视频 (可以拖动的GridView)附源码DEMO
- 模仿今日头条:频道管理
- Android 仿今日头条频道管理(下)(GridView之间Item的移动和拖拽)