美文网首页自定义android控件Android进阶之路
自定义FloatLayout实现带动画的悬浮视图

自定义FloatLayout实现带动画的悬浮视图

作者: Mersens | 来源:发表于2016-10-24 11:47 被阅读942次

    前言

    最近在项目开发中有账号关联和切换账号的需求,并且还要有相应的显示和隐藏动画,这时就用到了布局动画 LayoutTransition,LayoutTransition动画是基于ViewGroup的,当你调用View的setVisibility()方法来控制其View的显示或消失时就可以触发该动画,该动画有四中类型:
    1.APPEARING — 元素在容器中显现时需要动画显示
    2.CHANGE_APPEARING — 由于容器中要显现一个新的元素,其它元素的变化需要动画显示
    3.DISAPPEARING —元素在容器中消失时需要动画显示
    4.CHANGE_DISAPPEARING — 由于容器中某个元素要消失,其它元素的变化需要动画显示

    明白了LayoutTransition的基本使用,我们就可以通过自定义一个ViewGroup来实现该功能
    效果图如下:

    初始化状态 点击后展开状态

    实现

    FloatLayout全部代码:

    /**
     * Created by Mersens on 2016/10/21.
     */
    
    public class FloatLayout extends LinearLayout {
        private LayoutParams params = null;
        private Context context;
        private List<View> list = null;
        private static final int ANIMATION_TIME = 20;
        private boolean isMenuOpened;
        private int i = 0;
        private int j = 0;
        private ShowRunnable showRunnable = new ShowRunnable();
        private HideRunnable hideRunnable = new HideRunnable();
        private LayoutTransition layoutTransition;
        private LayoutInflater inflater;
        private OnItemClickListener onItemClickListener;
    
        public FloatLayout(Context context) {
            this(context, null);
        }
    
        public FloatLayout(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public FloatLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            this.context = context;
            init();
        }
    
        final Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
    
        private void init() {
            layoutTransition = new LayoutTransition();
            setLayoutTransition(layoutTransition);
            list = new ArrayList<>();
            inflater = LayoutInflater.from(context);
            params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
            params.gravity = Gravity.CENTER;
            params.rightMargin = 10;
            setGravity(Gravity.RIGHT);
            setBackgroundColor(getResources().getColor(android.R.color.transparent));
            ObjectAnimator animator1 = ObjectAnimator.ofFloat(null, View.TRANSLATION_X, 0F, 0F).
                    setDuration(layoutTransition.getDuration(LayoutTransition.APPEARING));
            layoutTransition.setAnimator(LayoutTransition.APPEARING, animator1);
            ObjectAnimator animator2 = ObjectAnimator.ofFloat(null, View.TRANSLATION_X, 0F, 0F).
                    setDuration(layoutTransition.getDuration(LayoutTransition.DISAPPEARING));
            layoutTransition.setAnimator(LayoutTransition.DISAPPEARING, animator2);
        }
    
        public void addMainItem(final ItemBean item) {
            View mainView = inflater.inflate(R.layout.list_item, null);
            ImageView imageView = (ImageView) mainView.findViewById(R.id.ItemImage);
            TextView tv_name = (TextView) mainView.findViewById(R.id.tvname);
            Glide.with(context).load(item.getImg()).into(imageView);
            tv_name.setText(item.getName());
            mainView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (isMenuOpened) {
                        hidePromotedActions();
                        isMenuOpened = false;
                    } else {
                        isMenuOpened = true;
                        showPromotedActions();
                    }
                }
            });
            addView(mainView, params);
        }
    
        public void addItem(List<ItemBean> items, OnItemClickListener onItemClickListener) {
            this.onItemClickListener = onItemClickListener;
            for (int i = 0; i < items.size(); i++) {
                View item = inflater.inflate(R.layout.list_item, null);
                ImageView imageView = (ImageView) item.findViewById(R.id.ItemImage);
                Glide.with(context).load(items.get(i).getImg()).into(imageView);
                TextView tv_name = (TextView) item.findViewById(R.id.tvname);
                tv_name.setText(items.get(i).getName());
                item.setOnClickListener(new MyOnClicKListener(i));
                item.setVisibility(View.GONE);
                list.add(item);
                addView(item, params);
            }
        }
    
        private void hidePromotedActions() {
            invalidate();
            handler.postDelayed(hideRunnable, ANIMATION_TIME);
        }
    
        private void showPromotedActions() {
            invalidate();
            handler.postDelayed(showRunnable, ANIMATION_TIME);
        }
    
    
        class HideRunnable implements Runnable {
            @Override
            public void run() {
                j++;
                if (j <= list.size()) {
                    list.get(j - 1).setVisibility(View.GONE);
                    handler.postDelayed(hideRunnable, ANIMATION_TIME);
                } else {
                    handler.removeCallbacks(hideRunnable);
                    j = 0;
                }
            }
        }
    
        class ShowRunnable implements Runnable {
            @Override
            public void run() {
                i++;
                if (i <= list.size()) {
                    list.get(i - 1).setVisibility(View.VISIBLE);
                    handler.postDelayed(showRunnable, ANIMATION_TIME);
                } else {
                    handler.removeCallbacks(showRunnable);
                    i = 0;
                }
            }
        }
    
        public class MyOnClicKListener implements OnClickListener {
            private int pos;
    
            public MyOnClicKListener(int pos) {
                this.pos = pos;
            }
    
            @Override
            public void onClick(View view) {
                onItemClickListener.onItemClick(pos);
    
            }
        }
    
        public interface OnItemClickListener {
             void onItemClick(int pos);
        }
    
    
    }
    
    
    

    list_item.xml文件

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/itemlayout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical" >
    
        <com.mersens.floatlayout.floatlayout.CircleImageView
            android:id="@+id/ItemImage"
            android:layout_width="32dp"
            android:layout_height="32dp"
            android:src="@mipmap/ic_launcher"
            android:layout_gravity="center_horizontal"
             >
        </com.mersens.floatlayout.floatlayout.CircleImageView>
    
        <TextView
            android:id="@+id/tvname"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="Title"
            android:textSize="12sp"
            >
        </TextView>
    
    </LinearLayout>
    

    这里使用了一个CircleImageView,该View是使用别人的控件,通过自定义ImageView来实现圆形的图片,网上搜索一下会有很多这样的例子,但你也不必这么做,如果你使用了Glide来加载图片的话,Glide本身就可以实现对图片的裁剪,达到圆形展示的效果,不过这里还是打算贴出CircleImageView的代码:

    public class CircleImageView extends ImageView {
    
        private static final ScaleType SCALE_TYPE = ScaleType.CENTER_INSIDE;
    
        private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
        private static final int COLORDRAWABLE_DIMENSION = 1;
    
        private static final int DEFAULT_BORDER_WIDTH = 4;
        private static final int DEFAULT_BORDER_COLOR = Color.WHITE;
    
        private final RectF mDrawableRect = new RectF();
        private final RectF mBorderRect = new RectF();
    
        private final Matrix mShaderMatrix = new Matrix();
        private final Paint mBitmapPaint = new Paint();
        private final Paint mBorderPaint = new Paint();
    
        private int mBorderColor = DEFAULT_BORDER_COLOR;
        private int mBorderWidth = DEFAULT_BORDER_WIDTH;
    
        private Bitmap mBitmap;
        private BitmapShader mBitmapShader;
        private int mBitmapWidth;
        private int mBitmapHeight;
    
        private float mDrawableRadius;
        private float mBorderRadius;
    
        private boolean mReady;
        private boolean mSetupPending;
    
        public CircleImageView(Context context) {
            super(context);
        }
    
        public CircleImageView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            super.setScaleType(SCALE_TYPE);
    
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);
    
            mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width, DEFAULT_BORDER_WIDTH);
            mBorderColor = a.getColor(R.styleable.CircleImageView_border_color, DEFAULT_BORDER_COLOR);
    
            a.recycle();
    
            mReady = true;
    
            if (mSetupPending) {
                setup();
                mSetupPending = false;
            }
        }
    
        @Override
        public ScaleType getScaleType() {
            return SCALE_TYPE;
        }
    
        @Override
        public void setScaleType(ScaleType scaleType) {
            if (scaleType != SCALE_TYPE) {
                throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
            }
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            if (getDrawable() == null) {
                return;
            }
    
            canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint);
            canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius, mBorderPaint);
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            setup();
        }
    
        public int getBorderColor() {
            return mBorderColor;
        }
    
        public void setBorderColor(int borderColor) {
            if (borderColor == mBorderColor) {
                return;
            }
    
            mBorderColor = borderColor;
            mBorderPaint.setColor(mBorderColor);
            invalidate();
        }
    
        public int getBorderWidth() {
            return mBorderWidth;
        }
    
        public void setBorderWidth(int borderWidth) {
            if (borderWidth == mBorderWidth) {
                return;
            }
    
            mBorderWidth = borderWidth;
            setup();
        }
    
        @Override
        public void setImageBitmap(Bitmap bm) {
            super.setImageBitmap(bm);
            mBitmap = bm;
            setup();
        }
    
        @Override
        public void setImageDrawable(Drawable drawable) {
            super.setImageDrawable(drawable);
            mBitmap = getBitmapFromDrawable(drawable);
            setup();
        }
    
        @Override
        public void setImageResource(int resId) {
            super.setImageResource(resId);
            mBitmap = getBitmapFromDrawable(getDrawable());
            setup();
        }
    
        private Bitmap getBitmapFromDrawable(Drawable drawable) {
            if (drawable == null) {
                return null;
            }
    
            if (drawable instanceof BitmapDrawable) {
                return ((BitmapDrawable) drawable).getBitmap();
            }
    
            try {
                Bitmap bitmap;
    
                if (drawable instanceof ColorDrawable) {
                    bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
                } else {
                    bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
                }
    
                Canvas canvas = new Canvas(bitmap);
                drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
                drawable.draw(canvas);
                return bitmap;
            } catch (OutOfMemoryError e) {
                return null;
            }
        }
    
        private void setup() {
            if (!mReady) {
                mSetupPending = true;
                return;
            }
    
            if (mBitmap == null) {
                return;
            }
    
            mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
    
            mBitmapPaint.setAntiAlias(true);
            mBitmapPaint.setShader(mBitmapShader);
    
            mBorderPaint.setStyle(Paint.Style.STROKE);
            mBorderPaint.setAntiAlias(true);
            mBorderPaint.setColor(mBorderColor);
            mBorderPaint.setStrokeWidth(mBorderWidth);
    
            mBitmapHeight = mBitmap.getHeight();
            mBitmapWidth = mBitmap.getWidth();
    
            mBorderRect.set(0, 0, getWidth(), getHeight());
            mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2, (mBorderRect.width() - mBorderWidth) / 2);
    
            mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth);
            mDrawableRadius = Math.min(mDrawableRect.height() / 2, mDrawableRect.width() / 2);
    
            updateShaderMatrix();
            invalidate();
        }
    
        private void updateShaderMatrix() {
            float scale;
            float dx = 0;
            float dy = 0;
    
            mShaderMatrix.set(null);
    
            if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
                scale = mDrawableRect.height() / (float) mBitmapHeight;
                dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
            } else {
                scale = mDrawableRect.width() / (float) mBitmapWidth;
                dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
            }
    
            mShaderMatrix.setScale(scale, scale);
            mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth, (int) (dy + 0.5f) + mBorderWidth);
    
            mBitmapShader.setLocalMatrix(mShaderMatrix);
        }
    
    }
    

    自定义属性attrs.xml文件

    <resources>
    
        <declare-styleable name="CircleImageView">
            <attr name="border_width" format="dimension" />
            <attr name="border_color" format="color" />
        </declare-styleable>
    
    </resources>
    

    ItemBean实体类

    /**
     * Created by Mersens on 2016/10/22.
     */
    
    public class ItemBean {
        private String name;
        private String img;
        public ItemBean(){
    
        }
        public ItemBean(String name, String img) {
            this.name = name;
            this.img = img;
        }
    
        public String getImg() {
            return img;
        }
    
        public void setImg(String img) {
            this.img = img;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
    }
    

    添加Glide的依赖

    compile 'com.github.bumptech.glide:glide:3.7.0'
    

    具体使用方法

    public class MainActivity extends AppCompatActivity {
        private FloatLayout floatLayout;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            init();
        }
        public void init(){
            floatLayout=(FloatLayout)findViewById(R.id.mFloatLayout);
            ItemBean mainItem=new ItemBean("小明","http://staging.topmd.cn/UploadFile/image/20160914103216400.jpg");
            floatLayout.addMainItem(mainItem);
            List<ItemBean> list=new ArrayList<>();
            list.add(new ItemBean("小王","http://staging.topmd.cn/UploadFile/image/20160923162109048.jpg"));
            list.add(new ItemBean("小强","http://staging.topmd.cn/UploadFile/image/20161012160235822.jpg"));
            floatLayout.addItem(list, new FloatLayout.OnItemClickListener() {
                @Override
                public void onItemClick(int pos) {
                    Toast.makeText(getApplicationContext(), "item"+pos, Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
    
    

    相关文章

      网友评论

        本文标题:自定义FloatLayout实现带动画的悬浮视图

        本文链接:https://www.haomeiwen.com/subject/ojqluttx.html