首页 > 代码库 > Android自定义Viewgroup切换View带有吸附效果

Android自定义Viewgroup切换View带有吸附效果

1.概述

先上效果图
技术分享

大概就是这个效果,先说说实现思路
1.首先我们要拿到图片的url(网络)或id、路径(本地),将View与数据进行绑定,写我们自己的Adapter
2.自定义Viewgroup将要显示的view进行布局,以及处理触摸事件进行逻辑处理
3.写切换回调

2.实现

1)自定义Adapter

这里我下载的网络图片,同样可以将图片放到res下设置ImageView的内容
public class DragPageViewAdapter {
    private static final String TAG = "DragPageViewAdapter";
    private List<WxCollocationRandomList> mDatas;
    private Context mContext;
    private LayoutInflater inflater;
    /**
     * 宽和高
     */
    int width;
    int height;

    public DragPageViewAdapter(Context mContext) {
        this.mContext = mContext;
        inflater = LayoutInflater.from(mContext);
        width = (int) (0.8*DragViewActivity.mScreenWidth);<span style="font-family: Arial, Helvetica, sans-serif;">//宽是屏幕宽度的0.8</span>
        height = (int) (0.8*DragViewActivity.mScreenWidth);

    }

    public int getCount(){
        return mDatas==null?0:mDatas.size();
    }

    public int getItemId(int position){
        return position;
    }

    public Object getItem(int position){
        return mDatas==null?null:mDatas.get(position);
    }

    public View getView(int position, View convertView, ViewGroup parent){
        LogHelper.d(TAG, "position :" + position);
        final ImageView imageView = new ImageView(mContext);
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width,height);
        params.addRule(Gravity.CENTER);
        imageView.setLayoutParams(params);
        imageView.setBackgroundDrawable(mContext.getResources().getDrawable(R.drawable.gray_border));
        ViewHelper.setAlpha(imageView,1.0f);

        String url = mDatas.get(position).getPictureUrl();
        if (!Utils.stringIsNull(url)){
            url = url.substring(0, url.lastIndexOf(".")) + "--300x300.png";
//            UILHelper.loadImageUrl(url, imageView,0);
            UILHelper.loadImageUrl(url, new SimpleImageLoadingListener(){
                @Override
                public void onl oadingComplete(String imageUri, View view, Bitmap loadedImage) {
                    imageView.setImageBitmap(loadedImage);
                }
            });
        }
        convertView = imageView;
        convertView.setTag(mDatas.get(position));
//        viewHolder.mImageView.setImageResource((Integer) mDatas.get(position));
        return convertView;
    }

    public void setData(List data){
        mDatas = data;
    }



    private class ViewHolder{
        ImageView mImageView;
    }
}

通过此Adapter将所需要的图片匆网络下载下来,当我们需要的时候ImageView自己去请求下载。并将数据通过setTag的方法与ImageView进行绑定

2)自定义Viewgroup

直接上代码,我们要做的事情
a.onMeasure方法测量子自己及子view
b.onLayout方法给子view进行布局
c.onTouchEvent处理触摸事件,up的时候判断是否切换下一个view,如果不是的话通过属性动画将view的状态恢复
d.绑定切换的回调
e.切换后把当前的View从viewgruop中remove掉,add一个需要加载的view,同时给当前view赋值为切换的view,保证viewgroup中不多于指定的子view个数,保证不OOM
public class DragPageView extends ViewGroup{
    private static final String TAG = "DragPageView";
    /**
     * 适配器
     */
    private DragPageViewAdapter mAdapter;
    /**
     * view的个数
     */
    private int ONE_SCREEN_COUNT = 2;
    /**
     * 屏幕的宽和高
     */
    private int mScrenWidth;
    private int mScreenHeight;
    /**
     * 子元素的宽 高
     */
    private int mChildWidth;
    private int mChildHeight;
    /**
     * 当前最后一张图片的index
     */
    private int mLastIndex;
    /**
     * 当前第一张图片的下标
     */
    private int mFirstIndex;
    /**
     * 当前显示View
     */
    private View mFirstView;

    /**
     * 之前触摸的x y
     */
    private int mDownX;
    private int mDownY;
    /**
     * 当前触摸的x y
     */
    private int mCurX;
    private int mCurY;

    public static int mDirection = -1;
    public static final int DIRECTION_LEFT_TO_RIGHT = 1;
    public static final int DIRECTION_RIGHT_TO_LEFT = 2;
    /**
     * 保存View与位置的键值对
     */
    private Map<Integer,View> mViewMap = new HashMap<Integer, View>();

    /**
     * 最大旋转角度
     */
    private int ROTATE_DEGREE = 30;

    private int CHILD_OFFSET = 10;
    /**
     * 保存回调接口
     */
    private OnImageSavedListener mOnImageSavedListener;

    public void setOnImageSavedListener(OnImageSavedListener mOnImageSavedListener) {
        this.mOnImageSavedListener = mOnImageSavedListener;
    }


    public DragPageView(Context context) {
        this(context, null);
    }

    public DragPageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DragPageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //获得屏幕宽和高
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics metrics = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(metrics);
        mScrenWidth = metrics.widthPixels;
        mScreenHeight = metrics.heightPixels;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        /**
         * 获得此ViewGroup上级容器为其推荐的宽和高,以及计算模式
         */
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
        int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);


        // 计算出所有的childView的宽和高
        measureChildren(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(mScrenWidth, sizeHeight);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        for (int i = 0; i < getChildCount(); i++){
            View child = getChildAt(i);
            mChildWidth = child.getMeasuredWidth();
            mChildHeight = child.getMeasuredHeight();

            int left = getWidth()/2-mChildWidth/2;
            int top = getHeight()/2- mChildHeight*2/3;
            int right = left + mChildWidth;
            int bottom = top + mChildHeight;
            if (i == 0)
                child.layout(left+CHILD_OFFSET, top+CHILD_OFFSET, right+CHILD_OFFSET, bottom+CHILD_OFFSET);
            else
                child.layout(left, top, right, bottom);
        }
    }

    public void initDatas(DragPageViewAdapter mAdapter){
        this.mAdapter = mAdapter;

        initView();
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private void initView() {
        removeAllViews();
        for (int i = 0; i < ONE_SCREEN_COUNT; i++){
            View view = mAdapter.getView(i, null, this);
            mViewMap.put(i,view);
        }
        //mViewMap中存放的view倒叙添加到ViewGroup,因为ViewGroup index = 0在最底层
        for (int j= ONE_SCREEN_COUNT -1; j>=0;j--){
            addView(mViewMap.get(j));
            if (j == 0){
                mFirstView = mViewMap.get(j);
            }
        }
        mLastIndex = ONE_SCREEN_COUNT -1;
        mFirstIndex = mLastIndex - ONE_SCREEN_COUNT-1;
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int focusX = (int) event.getX();
        int focusY = (int) event.getY();
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                mDownX = mCurX = focusX;
                mDownY = mCurY = focusY;
                break;
            case MotionEvent.ACTION_MOVE:
                final int deltaX = focusX - mCurX;
                final int deltaY = focusY-mCurY ;
                if (Math.abs(deltaX) > 5 || Math.abs(deltaY)>5){

                    if (focusX > mDownX){
                        mDirection = DIRECTION_LEFT_TO_RIGHT;
                    }else {
                        mDirection = DIRECTION_RIGHT_TO_LEFT;
                    }
                    handlerScroll(deltaX, deltaY);
//                mFirstView.setPivotX((float) (mFirstView.getMeasuredWidth()*0.5));
//                mFirstView.setPivotY(mFirstView.getMeasuredHeight());
                    mFirstView.setRotation(ROTATE_DEGREE*(focusX-mDownX)/mChildWidth);
                }
                mCurX = focusX;
                mCurY = focusY;
                break;
            case MotionEvent.ACTION_UP:
                LogHelper.d(TAG, "mPreX : " + mDownX + " mPreY " + mDownY + " mCurX : " + mCurX + " mCurY : " + mCurY);
                if (Math.abs(mCurX- mDownX)> mChildWidth/2 && mLastIndex < mAdapter.getCount()){
                    loadNextImage();
                }else {
                    resetView();
                }
                break;

        }
        return true;
    }

    private void resetView() {
        PropertyValuesHolder oaX= PropertyValuesHolder.ofFloat("translationX", 0);
        PropertyValuesHolder oaY = PropertyValuesHolder.ofFloat("translationY",0);
        PropertyValuesHolder rotate = PropertyValuesHolder.ofFloat("rotation",0);
        ObjectAnimator.ofPropertyValuesHolder(mFirstView, oaX, oaY, rotate).start();
    }

    public void loadNextImage() {
        ObjectAnimator oaX = null;
        ObjectAnimator oaRotate = null;
        if (mLastIndex == mAdapter.getCount()) return;
        if (mDirection == DIRECTION_LEFT_TO_RIGHT){
            oaX = new ObjectAnimator().ofFloat(mFirstView, "translationX",800);
            oaRotate = new ObjectAnimator().ofFloat(mFirstView, "rotation",ROTATE_DEGREE);
            //通知回调已保存的view
            if (mOnImageSavedListener != null){
                mOnImageSavedListener.onImageSaved(mFirstView);
            }
        }else {
            oaX = new ObjectAnimator().ofFloat(mFirstView, "translationX", -800);
            oaRotate = new ObjectAnimator().ofFloat(mFirstView, "rotation", -ROTATE_DEGREE);
            if (mOnImageSavedListener != null){
                mOnImageSavedListener.onImageDelete(mFirstView);
            }
        }
        oaX.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                //动画结束后load下一张图片
                mViewMap.remove(mFirstIndex);
                //删除view
                removeAllViews();
                //获取下一张图片
                if ((mLastIndex+1)>= mAdapter.getCount()){
                    addView(mViewMap.get(mLastIndex));
                }else {
                    View view = mAdapter.getView(++mLastIndex, null, DragPageView.this);
                    mViewMap.put(mLastIndex, view);
                    addView(mViewMap.get(mLastIndex));
                    addView(mViewMap.get(mLastIndex-1));
                    mFirstView = mViewMap.get(mLastIndex-1);
                }
            }
        });
        //开启动画
        oaRotate.start();
        oaX.start();
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private void handlerScroll(float distanceX, float distanceY) {
        int newLeft = (int) (mFirstView.getLeft()+distanceX);
        int newTop = (int) (mFirstView.getTop()+distanceY);

        float[] deltaVector = {distanceX, distanceY};
        mFirstView.getMatrix().mapVectors(deltaVector);
        mFirstView.setTranslationX(mFirstView.getTranslationX() + deltaVector[0]);
        mFirstView.setTranslationY(mFirstView.getTranslationY() + deltaVector[1]);
    }

    public interface OnImageSavedListener{
        public void onImageSaved(View view);
        public void onImageDelete(View view);
    }
}

3)Activity

负责的事情
a.请求数据初始化Adapter及ViewGroup
b.切换回调的响应
c.点击按键切换的事件处理
public class DragViewActivity extends BaseActivity{
    private static final String TAG = "DragViewActivity";
    private DragPageViewAdapter adapter;
    private DragPageView mDragView;
    private int pageIndex = 1;
    private int pageSize = 21;
    private int total;
    /**
     * userId
     */
    private String userId;
    //网路请求的数据
    private List<WxCollocationRandomList> lists;
    //保存的view的信息
    private List<WxCollocationRandomList> mSavedView;
    private WxCollocationRandomList mViewInfo;
    /**
     * 屏幕宽和高
     */
    public static int mScreenWidth;
    public static int mScreenHeight;

    private TextView mSkip;
    private TextView mSave;
    private TextView mDone;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.dragview_act);

        WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        DisplayMetrics displayMetrics = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(displayMetrics);
        mScreenWidth = displayMetrics.widthPixels;
        mScreenHeight = displayMetrics.heightPixels;
        userId = SettingsManager.getSettingsManager(this).getUserId();
        initView();

        httpGetImageInfo(pageIndex);
    }

    private void httpGetImageInfo(final int pageIndex) {
        JSONObject object = new JSONObject();
        try {
            object.put("pageIndex", pageIndex);
            object.put("pageSize", pageSize);
        } catch (JSONException e) {
            e.printStackTrace();
        }

        if (pageIndex == 1) {
            showProgress(getResources().getString(R.string.app_data_loading));
        }

        HttpsRequestUtil.doHttpsVolleyPost(this, SOAServices.COLLOCATION_SERVICE,
                SOAMethods.RANDOM_COLLOCATION, HttpsRequestUtil.GET, object, new Response.Listener<String>(){
                    @Override
                    public void onResponse(String response) {
                        if (pageIndex ==1){
                            closeProgress();
                        }
                        transformData(response);
                    }
                }, new Response.ErrorListener(){
                    @Override
                    public void one rrorResponse(VolleyError error) {
                        closeProgress();
                    }
                });
    }

    /**
     * 请求的数据,进行初始化adapter及Viewgroup的初始化
     * @param response
     */
    private void transformData(String response) {
        Gson gson = new Gson();
        WxCollocationRandomFilter wxCollocationRandomFilter= gson.fromJson(response, WxCollocationRandomFilter.class);
        total = wxCollocationRandomFilter.getTotal();
        if (total == 0)return;
        lists = wxCollocationRandomFilter.getCollocationRandomLists();
        adapter.setData(lists);

        mDragView.initDatas(adapter);
    }

    private void initView() {
        mDragView = (DragPageView) findViewById(R.id.dragView);
        adapter = new DragPageViewAdapter(this);


        mDragView.setOnImageSavedListener(new DragPageView.OnImageSavedListener() {
            @Override
            public void onImageSaved(View view) {
                mViewInfo = (WxCollocationRandomList) view.getTag();
                httpSaveFavorite(mViewInfo);
            }

            @Override
            public void onImageDelete(View view) {
                mViewInfo = (WxCollocationRandomList) view.getTag();
                httpDeleteFavorite(mViewInfo);
            }
        });



        mSkip = (TextView) findViewById(R.id.skip);
        mSave = (TextView) findViewById(R.id.save);
        mDone = (TextView) findViewById(R.id.done);

        mSkip.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mDragView.mDirection = mDragView.DIRECTION_RIGHT_TO_LEFT;
                mDragView.loadNextImage();
            }
        });

        mSave.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mDragView.mDirection = mDragView.DIRECTION_LEFT_TO_RIGHT;
                mDragView.loadNextImage();
            }
        });

        mDone.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                DragViewActivity.this.finish();
            }
        });
    }

    /**
     * 喜欢
     * @param mViewInfo  搭配信息 sourcE_TYPE 1 商品 2 搭配
     */
    private void httpSaveFavorite(WxCollocationRandomList mViewInfo) {
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put("sourcE_ID", mViewInfo.getId());
            jsonObject.put("userId",userId);
            jsonObject.put("creatE_USER", mViewInfo.getCreateUser());
            jsonObject.put("sourcE_TYPE", 2);
        } catch (JSONException e) {
            e.printStackTrace();
        }

        HttpsRequestUtil.doHttpsVolleyPost(DragViewActivity.this, SOAServices.ORDER_SERVICE, SOAMethods.FAVORITE_ADD,
                HttpsRequestUtil.POST, jsonObject, new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        LogHelper.d(TAG, "SaveFavorite Success!");
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void one rrorResponse(VolleyError error) {
                        LogHelper.d(TAG, "SaveFavorite Error!");
                    }
                });
    }

    /**
     * 删除喜欢
     * @param mViewInfo  搭配信息 sourcE_TYPE 1 商品 2 搭配
     */
    private void httpDeleteFavorite(WxCollocationRandomList mViewInfo) {
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put("sourcE_ID", mViewInfo.getId());
            jsonObject.put("userId",userId);
            jsonObject.put("creatE_USER", mViewInfo.getCreateUser());
            jsonObject.put("sourcE_TYPE", 2);
        } catch (JSONException e) {
            e.printStackTrace();
        }

        HttpsRequestUtil.doHttpsVolleyPost(DragViewActivity.this, SOAServices.ORDER_SERVICE, SOAMethods.FAVORITE_DELETE,
                HttpsRequestUtil.POST, jsonObject, new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        LogHelper.d(TAG, "DeleteFavorite Success!");
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void one rrorResponse(VolleyError error) {
                        LogHelper.d(TAG, "DeleteFavorite Error!");
                    }
                });
    }
}





最后的效果还不错,感兴趣的可以研究研究。
demo 下载地址 http://download.csdn.net/detail/hlglinglong/8359097

本文参考文章:
鸿洋大神博客的
http://blog.csdn.net/lmj623565791/article/details/38140505
http://blog.csdn.net/lmj623565791/article/details/38339817

Android自定义Viewgroup切换View带有吸附效果