gradeview可拖动效果实现

下面先上这次实现功能的效果图:(注:这个效果图没有拖拽的时候移动动画,DEMO里面有,可以下载看看)

一、开发心里历程

刚开始接触这个的时候,不知道要如何实现,去网上翻了一大堆资料,懂了个大概,就是目前可以找到的都是拖拽的时候,不带移动动画的,和线上的客户端交互效果相差甚远,在反反复复的尝试查看相关东西,大致的做了出来,目前在模拟器上似乎有一点小BUG,真机测试没有问题,就先放上来,如果发现问题在修改优化。代码反面,没有好好的修改调整,可能会有点乱,请见谅哈。

二、开发前的准备

1.了解重写View的相关知识,并且知道GridView的一些内部方法,如:怎么通过触摸的坐标获取对应的position等(这里我采用的是继承GridView控件)

2.了解屏幕触摸动作传递原理    这里我以前转载的一篇或许会有帮助:Android事件分发机制完全解析,带你从源码的角度彻底理解(全)

3.了解位移动画Animation,本DEMO中主要用到:TranslateAnimation  平移动画

4.了解WindowManager的窗口机制,这里的item拖拽等都要设计到这个。

5.了解SQLiteDatabase 以及SQLiteOpenHelper等数据库操作相关的类,本DEMO中主要用到数据库进行存储频道信息,如果你要用文档进行存储读取也可以。

三、开发思路

1.  获取数据库中频道的列表,如果为空,赋予默认列表,并存入数据库,之后通过对应的适配器赋给对应的GridView

2.  2个GridView--(1.DragGrid   2. OtherGridView)

DragGrid 用于显示我的频道,带有长按拖拽效果

OtherGridView用于显示更多频道,不带推拽效果

注:由于屏幕大小不一定,外层使用ScrollView,所以2者都要重写计算高度

3.  点击2个GridView的时候,根据点击的Item对应的position,获取position对应的view,进行创建一层移动的动画层

起始位置:点击的positiongetLocationInWindow()获取。终点位置:另一个GridView的最后个ITEM 的position + 1的位置。

并赋予移动动画,等动画结束后对2者对应的频道列表进行数据的remove和add操作。

4.  设置点击和拖动的限制条件,如  推荐  这个ITEM是不允许用户操作的。

5.  拖动的DragGrid的操作:

(1)长按获取长按的ITEM的position  -- dragPosition 以及对应的view ,手指触摸屏幕的时候,调用onInterceptTouchEvent来获取MotionEvent.ACTION_DOWN事件,获取对应的数据。由于这里是继承了GridView,所以长按时间可以通过setOnItemLongClickListener监听来执行,或则你也可以通过计算点击时间来监听是否长按。

(2)通过onTouchEvent(MotionEvent ev)来监听手指的移动和抬起动作。当它移动到 其它的item下面,并且下方的item对应的position  不等于 dragPosition,进行数据交换,并且2者之间的所有item进行移动动画,动画结束后,数据更替刷新界面。

(3) 抬起手后,清除掉拖动时候创建的view,让GridView中的数据显示。

6.  退出时候,将改变后的频道列表存入数据库。

四、流程图

下面是大体的流程图:

五、核心代码

点击进行添加删除:

/** GRIDVIEW对应的ITEM点击监听接口  */ 
 @Override 
 public void onItemClick(AdapterView<?> parent, final View view, final int position,long id) {  
 //如果点击的时候,之前动画还没结束,那么就让点击事件无效 
 if(isMove){  
 return;  
        }  
 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 ChannelItem channel = ((DragAdapter) parent.getAdapter()).getItem(position);//获取点击的频道内容 
                    otherAdapter.setVisible(false);  
 //添加到最后一个 
                    otherAdapter.addItem(channel);  
 new Handler().postDelayed(new Runnable() {  
 public void run() {  
 try {  
 int[] endLocation = new int[2];  
 //获取终点的坐标 
                                otherGridView.getChildAt(otherGridView.getLastVisiblePosition()).getLocationInWindow(endLocation);  
                                MoveAnim(moveImageView, startLocation , endLocation, channel,userGridView);  
                                userAdapter.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 ChannelItem channel = ((OtherAdapter) parent.getAdapter()).getItem(position);  
                userAdapter.setVisible(false);  
 //添加到最后一个 
                userAdapter.addItem(channel);  
 new Handler().postDelayed(new Runnable() {  
 public void run() {  
 try {  
 int[] endLocation = new int[2];  
 //获取终点的坐标 
                            userGridView.getChildAt(userGridView.getLastVisiblePosition()).getLocationInWindow(endLocation);  
                            MoveAnim(moveImageView, startLocation , endLocation, channel,otherGridView);  
                            otherAdapter.setRemove(position);  
                        } catch (Exception localException) {  
                        }  
                    }  
                }, 50L);  
            }  
 break;  
 default:  
 break;  
        }  
    }  

 移动动画:
<span style="font-size:14px;">private void MoveAnim(View moveView, int[] startLocation,int[] endLocation, final ChannelItem moveChannel,  
 final GridView clickGridView) {  
 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 AnimationListener() {  
 
 @Override 
 public void onAnimationStart(Animation animation) {  
                isMove = true;  
            }  
 
 @Override 
 public void onAnimationRepeat(Animation animation) {  
            }  
 
 @Override 
 public void onAnimationEnd(Animation animation) {  
                moveViewGroup.removeView(mMoveView);  
 // instanceof 方法判断2边实例是不是一样,判断点击的是DragGrid还是OtherGridView 
 if (clickGridView instanceof DragGrid) {  
                    otherAdapter.setVisible(true);  
                    otherAdapter.notifyDataSetChanged();  
                    userAdapter.remove();  
                }else{  
                    userAdapter.setVisible(true);  
                    userAdapter.notifyDataSetChanged();  
                    otherAdapter.remove();  
                }  
                isMove = false;  
            }  
        });  
    }</span>  

可拖拽的DragGrid代码:

public class DragGrid extends GridView {  
 /** 点击时候的X位置 */ 
 public int downX;  
 /** 点击时候的Y位置 */ 
 public int downY;  
 /** 点击时候对应整个界面的X位置 */ 
 public int windowX;  
 /** 点击时候对应整个界面的Y位置 */ 
 public int windowY;  
 /** 屏幕上的X */ 
 private int win_view_x;  
 /** 屏幕上的Y */ 
 private int win_view_y;  
 /** 拖动的里x的距离 */ 
 int dragOffsetX;  
 /** 拖动的里Y的距离 */ 
 int dragOffsetY;  
 /** 长按时候对应postion */ 
 public int dragPosition;  
 /** Up后对应的ITEM的Position */ 
 private int dropPosition;  
 /** 开始拖动的ITEM的Position */ 
 private int startPosition;  
 /** item高 */ 
 private int itemHeight;  
 /** item宽 */ 
 private int itemWidth;  
 /** 拖动的时候对应ITEM的VIEW */ 
 private View dragImageView = null;  
 /** 长按的时候ITEM的VIEW */ 
 private ViewGroup dragItemView = null;  
 /** WindowManager管理器 */ 
 private WindowManager windowManager = null;  
 /** */ 
 private WindowManager.LayoutParams windowParams = null;  
 /** item总量 */ 
 private int itemTotalCount;  
 /** 一行的ITEM数量 */ 
 private int nColumns = 4;  
 /** 行数 */ 
 private int nRows;  
 /** 剩余部分 */ 
 private int Remainder;  
 /** 是否在移动 */ 
 private boolean isMoving = false;  
 /** */ 
 private int holdPosition;  
 /** 拖动的时候放大的倍数 */ 
 private double dragScale = 1.2D;  
 /** 震动器 */ 
 private Vibrator mVibrator;  
 /** 每个ITEM之间的水平间距 */ 
 private int mHorizontalSpacing = 15;  
 /** 每个ITEM之间的竖直间距 */ 
 private int mVerticalSpacing = 15;  
 /* 移动时候最后个动画的ID */ 
 private String LastAnimationID;  
 
 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);  
    }  
 
 public void init(Context context) {  
        mVibrator = (Vibrator) context  
                .getSystemService(Context.VIBRATOR_SERVICE);  
 // 将布局文件中设置的间距dip转为px 
        mHorizontalSpacing = DataTools.dip2px(context, mHorizontalSpacing);  
    }  
 
 @Override 
 public boolean onInterceptTouchEvent(MotionEvent ev) {  
 // TODO Auto-generated method stub 
 if (ev.getAction() == MotionEvent.ACTION_DOWN) {  
            downX = (int) ev.getX();  
            downY = (int) ev.getY();  
            windowX = (int) ev.getX();  
            windowY = (int) ev.getY();  
            setOnItemClickListener(ev);  
        }  
 return super.onInterceptTouchEvent(ev);  
    }  
 
 @Override 
 public boolean onTouchEvent(MotionEvent ev) {  
 // TODO Auto-generated method stub 
 boolean bool = true;  
 if (dragImageView != null 
                && dragPosition != AdapterView.INVALID_POSITION) {  
 // 移动时候的对应x,y位置 
            bool = super.onTouchEvent(ev);  
 int x = (int) ev.getX();  
 int y = (int) ev.getY();  
 switch (ev.getAction()) {  
 case MotionEvent.ACTION_DOWN:  
                downX = (int) ev.getX();  
                windowX = (int) ev.getX();  
                downY = (int) ev.getY();  
                windowY = (int) ev.getY();  
 break;  
 case MotionEvent.ACTION_MOVE:  
                onDrag(x, y, (int) ev.getRawX(), (int) ev.getRawY());  
 if (!isMoving) {  
                    OnMove(x, y);  
                }  
 if (pointToPosition(x, y) != AdapterView.INVALID_POSITION) {  
 break;  
                }  
 break;  
 case MotionEvent.ACTION_UP:  
                stopDrag();  
                onDrop(x, y);  
                requestDisallowInterceptTouchEvent(false);  
 break;  
 
 default:  
 break;  
            }  
        }  
 return super.onTouchEvent(ev);  
    }  
 
 /** 在拖动的情况 */ 
 private void onDrag(int x, int y, 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 onDrop(int x, int y) {  
 // 根据拖动到的x,y坐标获取拖动位置下方的ITEM对应的POSTION 
 int tempPostion = pointToPosition(x, y);  
        dropPosition = tempPostion;  
        DragAdapter mDragAdapter = (DragAdapter) getAdapter();  
 // 显示刚拖动的ITEM 
        mDragAdapter.setShowDropItem(true);  
 // 刷新适配器,让对应的ITEM显示 
        mDragAdapter.notifyDataSetChanged();  
    }  
 
 /** 
     * 长按点击监听 
     * @param ev 
     */ 
 public void setOnItemClickListener(final MotionEvent ev) {  
        setOnItemLongClickListener(new OnItemLongClickListener() {  
 
 @Override 
 public boolean onItemLongClick(AdapterView<?> parent, View view,  
 int position, long id) {  
 int x = (int) ev.getX();// 长安事件的X位置 
 int y = (int) ev.getY();// 长安事件的y位置 
                startPosition = position;// 第一次点击的postion 
                dragPosition = position;  
 if (startPosition <= 1) {  
 return false;  
                }  
                ViewGroup dragViewGroup = (ViewGroup) getChildAt(dragPosition  
                        - getFirstVisiblePosition());  
                TextView dragTextView = (TextView) dragViewGroup  
                        .findViewById(R.id.text_item);  
                dragTextView.setSelected(true);  
                dragTextView.setEnabled(false);  
                itemHeight = dragViewGroup.getHeight();  
                itemWidth = dragViewGroup.getWidth();  
                itemTotalCount = DragGrid.this.getCount();  
 int row = itemTotalCount / nColumns;// 算出行数 
                Remainder = (itemTotalCount % nColumns);// 算出最后一行多余的数量 
 if (Remainder != 0) {  
                    nRows = row + 1;  
                } else {  
                    nRows = row;  
                }  
 // 如果特殊的这个不等于拖动的那个,并且不等于-1 
 if (dragPosition != AdapterView.INVALID_POSITION) {  
 // 释放的资源使用的绘图缓存。如果你调用buildDrawingCache()手动没有调用setDrawingCacheEnabled(真正的),你应该清理缓存使用这种方法。 
                    win_view_x = windowX - dragViewGroup.getLeft();// VIEW相对自己的X,半斤 
                    win_view_y = windowY - dragViewGroup.getTop();// VIEW相对自己的y,半斤 
                    dragOffsetX = (int) (ev.getRawX() - x);// 手指在屏幕的上X位置-手指在控件中的位置就是距离最左边的距离 
                    dragOffsetY = (int) (ev.getRawY() - y);// 手指在屏幕的上y位置-手指在控件中的位置就是距离最上边的距离 
                    dragItemView = dragViewGroup;  
                    dragViewGroup.destroyDrawingCache();  
                    dragViewGroup.setDrawingCacheEnabled(true);  
                    Bitmap dragBitmap = Bitmap.createBitmap(dragViewGroup  
                            .getDrawingCache());  
                    mVibrator.vibrate(50);// 设置震动时间 
                    startDrag(dragBitmap, (int) ev.getRawX(),  
                            (int) ev.getRawY());  
                    hideDropItem();  
                    dragViewGroup.setVisibility(View.INVISIBLE);  
                    isMoving = false;  
                    requestDisallowInterceptTouchEvent(true);  
 return true;  
                }  
 return false;  
            }  
        });  
    }  
 
 public void startDrag(Bitmap dragBitmap, int x, int y) {  
        stopDrag();  
        windowParams = new WindowManager.LayoutParams();// 获取WINDOW界面的 
 // Gravity.TOP|Gravity.LEFT;这个必须加 
        windowParams.gravity = Gravity.TOP | Gravity.LEFT;  
 // 得到preview左上角相对于屏幕的坐标 
        windowParams.x = x - win_view_x;  
        windowParams.y = y - win_view_y;  
 // 设置拖拽item的宽和高 
        windowParams.width = (int) (dragScale * dragBitmap.getWidth());// 放大dragScale倍,可以设置拖动后的倍数 
        windowParams.height = (int) (dragScale * dragBitmap.getHeight());// 放大dragScale倍,可以设置拖动后的倍数 
 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;  
 this.windowParams.format = PixelFormat.TRANSLUCENT;  
 this.windowParams.windowAnimations = 0;  
        ImageView iv = new ImageView(getContext());  
        iv.setImageBitmap(dragBitmap);  
        windowManager = (WindowManager) getContext().getSystemService(  
                Context.WINDOW_SERVICE);// "window" 
        windowManager.addView(iv, windowParams);  
        dragImageView = iv;  
    }  
 
 /** 停止拖动 ,释放并初始化 */ 
 private void stopDrag() {  
 if (dragImageView != null) {  
            windowManager.removeView(dragImageView);  
            dragImageView = null;  
        }  
    }  
 
 /** 在ScrollView内,所以要进行计算高度 */ 
 @Override 
 public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
 int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,  
                MeasureSpec.AT_MOST);  
 super.onMeasure(widthMeasureSpec, expandSpec);  
    }  
 
 /** 隐藏 放下 的ITEM */ 
 private void hideDropItem() {  
        ((DragAdapter) getAdapter()).setShowDropItem(false);  
    }  
 
 /** 获取移动动画 */ 
 public Animation getMoveAnimation(float toXValue, float toYValue) {  
        TranslateAnimation mTranslateAnimation = new TranslateAnimation(  
                Animation.RELATIVE_TO_SELF, 0.0F, Animation.RELATIVE_TO_SELF,  
                toXValue, Animation.RELATIVE_TO_SELF, 0.0F,  
                Animation.RELATIVE_TO_SELF, toYValue);// 当前位置移动到指定位置 
        mTranslateAnimation.setFillAfter(true);// 设置一个动画效果执行完毕后,View对象保留在终止的位置。 
        mTranslateAnimation.setDuration(300L);  
 return mTranslateAnimation;  
    }  
 
 /** 移动的时候触发 */ 
 public void OnMove(int x, int y) {  
 // 拖动的VIEW下方的POSTION 
 int dPosition = pointToPosition(x, y);  
 // 判断下方的POSTION是否是最开始2个不能拖动的 
 if (dPosition > 1) {  
 if ((dPosition == -1) || (dPosition == dragPosition)) {  
 return;  
            }  
            dropPosition = dPosition;  
 if (dragPosition != startPosition) {  
                dragPosition = startPosition;  
            }  
 int movecount;  
 // 拖动的=开始拖的,并且 拖动的 不等于放下的 
 if ((dragPosition == startPosition)  
                    || (dragPosition != dropPosition)) {  
 // 移需要移动的动ITEM数量 
                movecount = dropPosition - dragPosition;  
            } else {  
 // 移需要移动的动ITEM数量为0 
                movecount = 0;  
            }  
 if (movecount == 0) {  
 return;  
            }  
 
 int movecount_abs = Math.abs(movecount);  
 
 if (dPosition != dragPosition) {  
 // dragGroup设置为不可见 
                ViewGroup dragGroup = (ViewGroup) getChildAt(dragPosition);  
                dragGroup.setVisibility(View.INVISIBLE);  
 float to_x = 1;// 当前下方positon 
 float to_y;// 当前下方右边positon 
 // x_vlaue移动的距离百分比(相对于自己长度的百分比) 
 float x_vlaue = ((float) mHorizontalSpacing / (float) itemWidth) + 1.0f;  
 // y_vlaue移动的距离百分比(相对于自己宽度的百分比) 
 float y_vlaue = ((float) mVerticalSpacing / (float) itemHeight) + 1.0f;  
                Log.d("x_vlaue", "x_vlaue = " + x_vlaue);  
 for (int i = 0; i < movecount_abs; i++) {  
                    to_x = x_vlaue;  
                    to_y = y_vlaue;  
 // 像左 
 if (movecount > 0) {  
 // 判断是不是同一行的 
                        holdPosition = dragPosition + i + 1;  
 if (dragPosition / nColumns == holdPosition / nColumns) {  
                            to_x = -x_vlaue;  
                            to_y = 0;  
                        } else if (holdPosition % 4 == 0) {  
                            to_x = 3 * x_vlaue;  
                            to_y = -y_vlaue;  
                        } else {  
                            to_x = -x_vlaue;  
                            to_y = 0;  
                        }  
                    } else {  
 // 向右,下移到上,右移到左 
                        holdPosition = dragPosition - i - 1;  
 if (dragPosition / nColumns == holdPosition / nColumns) {  
                            to_x = x_vlaue;  
                            to_y = 0;  
                        } else if ((holdPosition + 1) % 4 == 0) {  
                            to_x = -3 * x_vlaue;  
                            to_y = y_vlaue;  
                        } else {  
                            to_x = x_vlaue;  
                            to_y = 0;  
                        }  
                    }  
                    ViewGroup moveViewGroup = (ViewGroup) getChildAt(holdPosition);  
                    Animation moveAnimation = getMoveAnimation(to_x, to_y);  
                    moveViewGroup.startAnimation(moveAnimation);  
 // 如果是最后一个移动的,那么设置他的最后个动画ID为LastAnimationID 
 if (holdPosition == dropPosition) {  
                        LastAnimationID = moveAnimation.toString();  
                    }  
                    moveAnimation.setAnimationListener(new AnimationListener() {  
 
 @Override 
 public void onAnimationStart(Animation animation) {  
 // TODO Auto-generated method stub 
                            isMoving = 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)) {  
                                DragAdapter mDragAdapter = (DragAdapter) getAdapter();  
                                mDragAdapter.exchange(startPosition,  
                                        dropPosition);  
                                startPosition = dropPosition;  
                                dragPosition = dropPosition;  
                                isMoving = false;  
                            }  
                        }  
                    });  
                }  
            }  
        }  
    }  
}  

数据库SQLHelper文件

public class SQLHelper extends SQLiteOpenHelper {  
 public static final String DB_NAME = "database.db";// 数据库名称 
 public static final int VERSION = 1;  
 
 public static final String TABLE_CHANNEL = "channel";//数据表  
 
 public static final String ID = "id";// 
 public static final String NAME = "name";  
 public static final String ORDERID = "orderId";  
 public static final String SELECTED = "selected";  
 private Context context;  
 public SQLHelper(Context context) {  
 super(context, DB_NAME, null, VERSION);  
 this.context = context;  
    }  
 
 public Context getContext(){  
 return context;  
    }  
 
 @Override 
 public void onCreate(SQLiteDatabase db) {  
 // TODO 创建数据库后,对数据库的操作 
        String sql = "create table if not exists "+TABLE_CHANNEL +  
 "(_id INTEGER PRIMARY KEY AUTOINCREMENT, " +  
                ID + " INTEGER , " +  
                NAME + " TEXT , " +  
                ORDERID + " INTEGER , " +  
                SELECTED + " SELECTED)";  
        db.execSQL(sql);  
    }  
 
 @Override 
 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  
 // TODO 更改数据库版本的操作 
        onCreate(db);  
    }  
 
}  

注:本DEMO中,加入了长按震动,所以在权限里面记得加上“

<!-- 在SDCard中创建与删除文件权限 -->  
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />  
<!-- 往SDCard写入数据权限 -->  
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  
<!-- 震动权限 -->  
<uses-permission android:name="android.permission.VIBRATE"/>  

六、源码下载

源码DEMO下载地址如下:下载地址

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏肖蕾的博客

Android网络请求慢的时候,任意位置弹出加载中提示

14850
来自专栏程序员的诗和远方

看代码学AndroidUI - Tab

最近慢慢学习一点安卓,先看了些基础的,还处于很初级的阶段,平常都是面对弱类型的语言,python,js,现在看java突然有点不适应。 这里推荐郭神的《第一行代...

33990
来自专栏androidBlog

自定义 Behavior - 仿新浪微博发现页的实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/de...

15320
来自专栏分享达人秀

GridView属性和使用方法

前面一共用了8期来学习ListView列表的相关操作,其实学习的ListView的知识完全适用于AdapterView的其他子类,如GridView、S...

33370
来自专栏向治洪

actionbar详解(二)

经过前面两篇文章的学习,我想大家对ActionBar都已经有一个相对较为深刻的理解了。唯一欠缺的是,前面我们都只是学习了理论知识而已,虽然知识点已经掌握了,但是...

20280
来自专栏Android中高级开发

Android开发之漫漫长途 XIV——ListView

该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列。该系列引用了《Android开发艺术探索...

12430
来自专栏Android干货园

Android自定义组合控件---教你如何自定义下拉刷新和左滑删除

版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/80...

22010
来自专栏Android先生

RecyclerView 梳理:点击&amp;长按事件、分割线、拖曳排序、滑动删除

这次主要是把 RecyclerView 比较常用的基本的点,在这里集中整理一下。从这篇文章主要梳理以下几点:

25930
来自专栏xingoo, 一个梦想做发明家的程序员

【插件开发】—— 5 SWT控件以及布局使用

前文回顾: 1 插件学习篇 2 简单的建立插件工程以及模型文件分析 3 利用扩展点,开发透视图 4 SWT编程须知   经过前几篇的介绍,多少对S...

21290
来自专栏GIS讲堂

用百度地图API打造方便自己使用的手机地图

有钱人咱就不说了,因为偶是个穷银……因为穷,所以去年买的Huawei C8650+到现在还在上岗,对于没有钱买好的配置的手机的童鞋来说,类似于百度,谷歌,高德等...

65140

扫码关注云+社区

领取腾讯云代金券