自定义ViewGroup动态添加删除Tab

公司的手机OS升级,在最新的Camera拟用这样的交互。感觉还不错。
这里写图片描述
思路:
1.ViewGroup中包含imageView按钮,根据显示的个数动态计算布局,在onLayout中重新排布ImageView的位置。
2.重新排布ImageView的位置的时候使用动画,动画需要坐标即需要自定义ImageView添加位置属性。
3.提供给外界方法用以决定显示哪个按钮,显示几个。

下面先来给imageView控件添加坐标属性。
public class ItemView extends ImageView
{
    private int mLastStaus = View.VISIBLE;
    private float mLastPosition = 0;
    private float mCurrentPosition = 0;
    public ItemView(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);        
    }

    public ItemView(Context context, AttributeSet attrs)
    {
        super(context, attrs);  
    }

    public ItemView(Context context)
    {
        super(context);     
    }

    public float getmLastPosition()
    {
        return mLastPosition;
    }

    public void setmLastPosition(float mLastPosition)
    {
        this.mLastPosition = mLastPosition;
    }

    /**
     * @return the mLastStaus
     */
    public int getmLastStaus()
    {
        return mLastStaus;
    }

    /**
     * @param mLastStaus the mLastStaus to set
     */
    public void setmLastStaus(int mLastStaus)
    {
        this.mLastStaus = mLastStaus;
    }

    /**
     * @return the mCurrentPosition
     */
    public float getmCurrentPosition()
    {
        return mCurrentPosition;
    }

    /**
     * @param mCurrentPosition the mCurrentPosition to set
     */
    public void setmCurrentPosition(float mCurrentPosition)
    {
        this.mCurrentPosition = mCurrentPosition;
    }
}    

现在imageView已经有了mLastPosition mCurrentPosition 这个的位置属性。

现在定义ViewGroup以动态添加删除按钮。新控件继承LinearLayout方便实现

JPowerTranslateView extends LinearLayout

然后在XML中添加控件及按钮

 <LinearLayout 
            android:layout_width="match_parent"
            android:layout_height="56dp"
            android:orientation="horizontal"
            android:layout_alignParentTop="true">
        <Button 
            android:id="@+id/button1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Button1"/>

        <Button 
            android:id="@+id/button2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Button2"/>
    </LinearLayout>

    <com.ws.jpower.animation.JPowerTranslateView
            android:id="@+id/animation_main_view"
            android:layout_width="match_parent"
            android:layout_height="56dp"
            android:background="#50ffffff"
            android:orientation="horizontal"
            android:layout_centerInParent="true">
        <com.ws.jpower.animation.ItemView 
            android:id="@+id/image_btn1"
            android:layout_width="80dp"
            android:layout_height="match_parent"
            android:scaleType="center"
            android:src="@drawable/ic_lava_flash_auto_normal"/>


        <com.ws.jpower.animation.ItemView 
            android:id="@+id/image_btn2"
            android:layout_width="80dp"
            android:layout_height="match_parent"
            android:src="@drawable/lava_hdr_auto_normal"
            android:scaleType="center"/>

        <com.ws.jpower.animation.ItemView 
            android:id="@+id/image_btn3"
            android:layout_width="80dp"
            android:layout_height="match_parent"
            android:scaleType="center"
            android:src="@drawable/lava_shutter_type_gesture_5s_normal"/>

        <com.ws.jpower.animation.ItemView 
            android:id="@+id/image_btn4"
            android:layout_width="80dp"
            android:layout_height="match_parent"
            android:scaleType="center"
            android:src="@drawable/lava_switch_camera_normal"
            android:visibility="gone"/>
    </com.ws.jpower.animation.JPowerTranslateView>          
</RelativeLayout>

布局添加完后,在onFinishInflate方法中把GONE与VISIBLE的View分别放进不同的ArrayList

    @Override
    protected void onFinishInflate()
    {     
        super.onFinishInflate();
        mGoneView = new ArrayList<ItemView>();
        mVisibledView = new ArrayList<ItemView>();
        mVisibleView = new ArrayList<ItemView>();
        for (int i = 0; i < getChildCount(); i++)
        {
            if (getChildAt(i).getVisibility() == View.GONE)
            {
                mGoneView.add((ItemView) getChildAt(i));
            } else
            {
                mVisibledView.add((ItemView) getChildAt(i));
                mVisibleView.add((ItemView) getChildAt(i));
            }
        }

    }

然后根据需要显示的View,从新排布

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = getMeasuredWidth();
        if (mWidth == 0)
        {
            mWidth = mDisplayWith;
        }
    }
    //排布子View
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b)
    {      
        super.onLayout(changed, l, t, r, b);
        if (mVisibleView != null && mVisibleView.size() > 0)
        {
            int visibleCount = mVisibleView.size();//需要显示View的数量
            itemWidth = mWidth / visibleCount;     //等分父容器
            for (int i = 0; i < visibleCount; i++)
            {
                int itemWh = 0;
                ItemView itemView = mVisibleView.get(i);
                itemWh = itemView.getWidth();
                if (itemWh == 0)
                {
                    itemWh = 80;
                }

                int maginLeft = itemWidth * i + (itemWidth - itemWh) / 2;//子View的left坐标,即把每个子View放置在每份的中间

                itemView.layout(maginLeft, itemView.getTop(), maginLeft + itemWh, itemView.getBottom());
                itemView.setmLastPosition(maginLeft);//保存位置信息,便于后期根据坐标做动画 LastPosition
            }
        }
    }

给外界提供一个方法,动态隐藏添加按钮。
先回顾一下动画demo

// 原始平移动画TranslateAnimation
    private Animation getAnimation(float fromXValue, float toXValue, float fromYValue, float toYValue, long duration)
    {
        /*TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta,             float toYDelta)
       float fromXDelta 动画开始的点离当前View X坐标上的差值
       float toXDelta 动画结束的点离当前View X坐标上的差值
       float fromYDelta 动画开始的点离当前View Y坐标上的差值
       float toYDelta 动画开始的点离当前View Y坐标上的差值*/
        TranslateAnimation animation = new TranslateAnimation(fromXValue, toXValue, fromYValue, toYValue);
        animation.setDuration(duration);
        animation.setInterpolator(new AccelerateInterpolator());
        return animation;
    }

    // 属性动画PropertyValuesHolder
    public void propertyValuesHolder(View view, float fromXValue, float toXValue, float fromYValue, float toYValue, long duration)
    {
        PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("translationX", fromXValue, toXValue);
        PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleY", fromYValue, toYValue);

        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY);
        animator.setDuration(duration).start();
        animator.addListener(new AnimatorListenerAdapter()
        {
            @Override
            public void onAnimationEnd(Animator animation)
            {

            }

        });
    }

    // 属性动画ObjectAnimator,AnimatorSet
    public void togetherAnimator(View view)
    {
        ObjectAnimator anim1 = ObjectAnimator.ofFloat(view, "alpha", 1.0f, 2f);
        ObjectAnimator anim2 = ObjectAnimator.ofFloat(view, "rotation", 1.0f, 2f);
        AnimatorSet animSet = new AnimatorSet();
        animSet.setDuration(2000);
        animSet.setInterpolator(new LinearInterpolator());
        // 两个动画同时执行
        animSet.playTogether(anim1, anim2);
        animSet.start();
    }

updateView方法

//status数组决定哪个View是显示的,visibleCount决定要显示的数目
    public void updateView(boolean[] status, int visibleCount)
    {
        Log.e("updateView", "updateView start ");
        mGoneView = new ArrayList<ItemView>();
        mVisibledView = new ArrayList<ItemView>();
        mVisiblingView = new ArrayList<ItemView>();
        mVisibleView = new ArrayList<ItemView>();

        itemWidth = mWidth / visibleCount;//重新计算每份的长度
        Log.e("updateView", "onLayout itemWidth = " + itemWidth);
        Log.e("updateView", "onLayout visibleCount = " + visibleCount);
        int count = 0;
        for (int i = 0; i < status.length; i++)
        {
            final ItemView itemView = (ItemView) getChildAt(i);
            if (status[i])//根据boolean,这是要显示的View
            {
                int itemWh = 0;
                itemWh = itemView.getWidth();
                if (itemWh == 0)
                {
                    itemWh = 80;
                }
                int maginLeft = itemWidth * count + (itemWidth - itemWh) / 2;//View的left坐标
                count++;
                itemView.setmCurrentPosition(maginLeft);//保存位置信息,便于后期根据坐标做动画  CurrentPosition
                Log.e("updateView", "updateView setmCurrentPosition " + maginLeft + " , i = " + i);
                if (itemView.getVisibility() == View.VISIBLE)
                {
                    // ((ItemView)getChildAt(i)).setmLastPosition(getChildAt(i).getLeft());
                    Log.e("updateView", "updateView getChildAt(i).getLeft() " + itemView.getLeft() + " , i = " + i);
                    mVisibledView.add(itemView);//已经显示了,本来就是显示的

                } else
                {
                    mVisiblingView.add(itemView);//本来没显示,这次要显示的
                }

                mVisibleView.add(itemView);

            } else
            {
                mGoneView.add(itemView);//本次需要隐藏的

                if (itemView.getVisibility() != View.GONE)
                {
                    itemView.setVisibility(View.GONE);

                }
            }
        }
        //准备完数据后开始动画
        for (int i = 0; i < mVisibleView.size(); i++)
        {
            if (mVisibledView.contains(mVisibleView.get(i)))  //本来就显示的做平移动画
            {
                Log.e("updateView", "updateView getChildAt(i).getmCurrentPosition() " + mVisibleView.get(i).getmCurrentPosition());
                Log.e("updateView", "updateView getChildAt(i).getmLastPosition() " + mVisibleView.get(i).getmLastPosition());
               ObjectAnimator
                          .ofFloat(mVisibleView.get(i), "translationX",
                                (mVisibleView.get(i).getmLastPosition() - mVisibleView.get(i).getmCurrentPosition()), 0f).setDuration(500).start();
            } else//未显示的setVisibility(View.VISIBLE)
            {
                mVisibleView.get(i).setVisibility(View.VISIBLE);
                AlphaAnimation mAlphaAnimation = new AlphaAnimation(0.0f, 1.0f);
                mAlphaAnimation.setDuration(200);
                mAlphaAnimation.setInterpolator(new AccelerateInterpolator());
                mVisibleView.get(i).startAnimation(mAlphaAnimation);
            }
        }
        Log.e("updateView", "updateView end ");

    }

下面是使用demo:

public class AnimationActivity extends Activity
{
    private Button button1 = null;
    private Button button2 = null;

    private JPowerTranslateView mJPowerTranslateView;
    boolean isReset = false;
    boolean isReset2 = false;

    int visibleCount = 4;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.animation_activity);
        mJPowerTranslateView = (JPowerTranslateView) findViewById(R.id.animation_main_view);
        mJPowerTranslateView.setBackgroundResource(R.drawable.lava_pro_single_settings_bg);

        button1 = (Button) findViewById(R.id.button1);
        button2 = (Button) findViewById(R.id.button2);
        button1.setOnClickListener(new OnClickListener()
        {

            @Override
            public void onClick(View v)
            {
                // TODO Auto-generated method stub
                boolean status[] =
                { true, true, false, true };
                boolean reset[] =
                { true, true, true, true };
                if (isReset)
                {

                    mJPowerTranslateView.updateView(status, 3);
                    isReset = false;
                } else
                {

                    isReset = true;
                    mJPowerTranslateView.updateView(reset, 4);
                }
                // mJPowerTranslateView.updateView();
            }
        });
        button2.setOnClickListener(new OnClickListener()
        {

            @Override
            public void onClick(View v)
            {
                // TODO Auto-generated method stub
                boolean status[] =
                { true, false, true, false };
                boolean reset[] =
                { false, true, false, false };
                if (isReset)
                {
                    mJPowerTranslateView.updateView(status, 2);
                    isReset = false;
                } else
                {
                    mJPowerTranslateView.updateView(reset, 1);
                    isReset = true;
                }
                // mJPowerTranslateView.updateView();
            }
        });
    }
}

JPowerTranslateView 完整代码


import java.util.ArrayList;
import java.util.List;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.LinearInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;

public class JPowerTranslateView extends LinearLayout
{

    private int mDisplayWith = 720;
    private int mWidth = 720;
    private int itemWidth;
    int visibleCount = 4;
    private List<ItemView> mGoneView = null;
    private List<ItemView> mVisibledView = null;
    private List<ItemView> mVisiblingView = null;
    private List<ItemView> mVisibleView = null;

    public JPowerTranslateView(Context context)
    {
        super(context, null);
    }
    public JPowerTranslateView(Context context, AttributeSet attrs)
    {
        this(context, attrs, 0);     
    }

    public JPowerTranslateView(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        mDisplayWith = wm.getDefaultDisplay().getWidth();
    }
   //初始化 把GONE与VISIBLE的View分别放进不同的ArrayList
    @Override
    protected void onFinishInflate()
    {
        // TODO Auto-generated method stub
        super.onFinishInflate();
        mGoneView = new ArrayList<ItemView>();
        mVisibledView = new ArrayList<ItemView>();
        mVisibleView = new ArrayList<ItemView>();
        for (int i = 0; i < getChildCount(); i++)
        {
            if (getChildAt(i).getVisibility() == View.GONE)
            {
                mGoneView.add((ItemView) getChildAt(i));
            } else
            {
                mVisibledView.add((ItemView) getChildAt(i));
                mVisibleView.add((ItemView) getChildAt(i));
            }
        }

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = getMeasuredWidth();

        if (mWidth == 0)
        {
            mWidth = mDisplayWith;
        }
    }
    //排布子View
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b)
    {      
        super.onLayout(changed, l, t, r, b);
        if (mVisibleView != null && mVisibleView.size() > 0)
        {
            int visibleCount = mVisibleView.size();//需要显示View的数量
            itemWidth = mWidth / visibleCount;     //等分父容器

            for (int i = 0; i < visibleCount; i++)
            {
                int itemWh = 0;
                ItemView itemView = mVisibleView.get(i);
                itemWh = itemView.getWidth();
                if (itemWh == 0)
                {
                    itemWh = 80;
                }

                int maginLeft = itemWidth * i + (itemWidth - itemWh) / 2;//子View的left坐标,即把每个子View放置在每份的中间

                itemView.layout(maginLeft, itemView.getTop(), maginLeft + itemWh, itemView.getBottom());
                itemView.setmLastPosition(maginLeft);//保存位置信息,便于后期根据坐标做动画 LastPosition
            }
        }  
    }

       //status数组决定哪个View是显示的,visibleCount决定要显示的数目
    public void updateView(boolean[] status, int visibleCount)
    {
        Log.e("updateView", "updateView start ");
        mGoneView = new ArrayList<ItemView>();
        mVisibledView = new ArrayList<ItemView>();
        mVisiblingView = new ArrayList<ItemView>();
        mVisibleView = new ArrayList<ItemView>();

        itemWidth = mWidth / visibleCount;//重新计算每份的长度
        int count = 0;
        for (int i = 0; i < status.length; i++)
        {
            final ItemView itemView = (ItemView) getChildAt(i);
            if (status[i])//根据boolean,这是要显示的View
            {
                int itemWh = 0;
                itemWh = itemView.getWidth();
                if (itemWh == 0)
                {
                    itemWh = 80;
                }
                int maginLeft = itemWidth * count + (itemWidth - itemWh) / 2;//View的left坐标
                count++;
                itemView.setmCurrentPosition(maginLeft);//保存位置信息,便于后期根据坐标做动画  CurrentPosition

                if (itemView.getVisibility() == View.VISIBLE)
                {
                    // ((ItemView)getChildAt(i)).setmLastPosition(getChildAt(i).getLeft());

                    mVisibledView.add(itemView);//已经显示了,本来就是显示的

                } else
                {
                    mVisiblingView.add(itemView);//本来没显示,这次要显示的
                }

                mVisibleView.add(itemView);

            } else
            {
                mGoneView.add(itemView);//本次需要隐藏的

                if (itemView.getVisibility() != View.GONE)
                {
                    itemView.setVisibility(View.GONE);         
                }
            }
        }
        //准备完数据后开始动画
        for (int i = 0; i < mVisibleView.size(); i++)
        {
            if (mVisibledView.contains(mVisibleView.get(i)))  //本来就显示的做平移动画
            {

               ObjectAnimator
                          .ofFloat(mVisibleView.get(i), "translationX",
                                (mVisibleView.get(i).getmLastPosition() - mVisibleView.get(i).getmCurrentPosition()), 0f).setDuration(500).start();

            } else//未显示的setVisibility(View.VISIBLE)
            {
                mVisibleView.get(i).setVisibility(View.VISIBLE);
                AlphaAnimation mAlphaAnimation = new AlphaAnimation(0.0f, 1.0f);
                mAlphaAnimation.setDuration(200);
                mAlphaAnimation.setInterpolator(new AccelerateInterpolator());
                mVisibleView.get(i).startAnimation(mAlphaAnimation);
            }
        }
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android自定义ViewGroup是指在Android开发中,通过继承ViewGroup类来创建自定义的布局容器。自定义ViewGroup可以用于实现一些特殊的布局效果,比如侧滑菜单、滑动卡片等等。通过自定义ViewGroup,我们可以更灵活地控制子视图的布局和交互行为,以满足特定的需求。自定义ViewGroup的实现主要包括重写onMeasure()方法和onLayout()方法,来测量和布局子视图。同时,我们还可以通过重写onInterceptTouchEvent()方法和onTouchEvent()方法来处理触摸事件,实现自定义的交互效果。如果你对自定义ViewGroup还不是很了解,或者正想学习如何自定义,可以参考相关的教程和文档,如引用\[1\]和引用\[2\]所提到的博客和官方文档。 #### 引用[.reference_title] - *1* [Android 手把手教您自定义ViewGroup(一)](https://blog.csdn.net/iteye_563/article/details/82601716)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [使用LayoutParams自定义安卓ViewGroup](https://blog.csdn.net/lfq88/article/details/127268493)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Android自定义ViewGroup](https://blog.csdn.net/farsight2009/article/details/62046643)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值