The implementation of Android carousel pictures

Posted by dink87522 on Sun, 05 Jan 2020 09:46:08 +0100

I. principle

First of all, put these pictures and some text to be rotated in different data sets. When the program starts, a set of pictures and text data will be displayed by default, and then a timer will be started to replace the displayed pictures and text data every other period of time. At the same time, some animation effects will be added to achieve the effect of rotation. At the same time, we also need to achieve the effect of rotating the pictures by sliding the fingers.

Two, implementation

1. Program start interface MainActivity

public class MainActivity extends AppCompatActivity implements ImageBannerFramLayout.FramLayoutLisenner{
    private ImageBannerFramLayout mGroup;
    private int[] ids = new int[] {
            R.drawable.i1,//Picture resource 1
            R.drawable.i2,//Picture resource 2
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //Calculate the current phone width
        DisplayMetrics displayMetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        int width = displayMetrics.widthPixels;
        mGroup = (ImageBannerFramLayout) findViewById(R.id.image_group);
        mGroup.setLisenner(this);
        List<Bitmap> list = new ArrayList<>();
        for (int i = 0; i < ids.length; i++) {
            Bitmap bitmap = BitmapFactory.decodeResource(getResources(),ids[i]);
            list.add(bitmap);
        }
        mGroup.addBitmaps(list);
    }

    @Override
    public void chickImageIndex(int pos) {
        Toast.makeText(this,"Index value = " + pos,Toast.LENGTH_SHORT).show();
    }
}

2. Create two new classes under new package view

1) New imagebarnerviewgroup class inherits from ViewGroup

public class ImageBarnnerViewGroup extends ViewGroup {
    private int children;//Total number of subviews of our View Group
    private int childwidth;//Width of subview
    private int childheight;//Height of subview

    private int x;//At this time, the value of x represents the abscissa of the first pressed position, and the abscissa of the position before each moving process
    private int index = 0;//Index for each image
    private Scroller scroller;

    /**
     * Use a click variable switch to judge, and judge that the user's operation is click at the moment of leaving the screen
     */
    private boolean isClick;//Click event when true, not click event when false
    private ImageBarnnerLister lister;

    private ImageBarnnerViewGroupLisnner barnnerViewGroupLisnner;

    public ImageBarnnerLister getLister() {
        return lister;
    }

    public void setLister(ImageBarnnerLister lister) {
        this.lister = lister;
    }

    public ImageBarnnerViewGroupLisnner getBarnnerViewGroupLisnner() {
        return barnnerViewGroupLisnner;
    }
    public void setBarnnerViewGroupLisnner(ImageBarnnerViewGroupLisnner barnnerViewGroupLisnner) {
        this.barnnerViewGroupLisnner = barnnerViewGroupLisnner;
    }
    public interface ImageBarnnerLister {
        void chickImageIndex(int pos);//pos represents the specific index value of our current image
    }
    /**
     * To achieve the effect of circle point switching at the bottom of the rotation chart
     * Customize a layout inherited from fragmenlayout, and take advantage of FragmeLayout layout features
     */
    
    //Automatic wheel broadcasting
    private boolean isAuto = true;//Turn on carousel by default
    private Timer timer = new Timer();
    private TimerTask timerTask;
    
    @SuppressLint("HandlerLeak")
    private android.os.Handler autohandler = new android.os.Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 0://We need automatic rotation of pictures
                    if (++index >= children) {//If it's the last picture, start with the first one
                        index = 0;
                    }
                    scrollTo(childwidth * index,0);
                    barnnerViewGroupLisnner.selectImage(index);
                    break;
                default:
            }
        }
    };

    private void startAuto() {
        isAuto = true;

    }

    private void stopAuto() {
        isAuto = false;
    }

    /**
     * Use Timer, TimerTask and Handler to realize automatic rotation
     */

    public ImageBarnnerViewGroup(Context context) {
        super(context);
        initObj();
    }

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

    public ImageBarnnerViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initObj();
    }


    private void initObj() {
        scroller = new Scroller(getContext());

        timerTask = new TimerTask() {
            @Override
            public void run() {
                if (isAuto) {//Turn on the carousel
                    autohandler.sendEmptyMessage(0);
                }
            }
        };
        timer.schedule(timerTask,100,3000);
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (scroller.computeScrollOffset()) {
            scrollTo(scroller.getCurrX(),0);
            invalidate();//Repaint
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //1. Find the number of subviews
        children = getChildCount();//We can know the number of self attempts
        if (0 == children)
        {
            setMeasuredDimension(0,0);
        } else {
            measureChildren(widthMeasureSpec, heightMeasureSpec);
            //At this time, we take the first subview = as the benchmark, that is, our View Group
            View view = getChildAt(0);
            childwidth = view.getMeasuredWidth();
            childheight = view.getMeasuredHeight();
            int width = view.getMeasuredWidth() * children;
            setMeasuredDimension(width,childheight);
        }
        //2. Measure the width and height of the subview
        //3. Calculate the width and height of the ViewGroup according to the gambling and height of the subview
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return super.onInterceptTouchEvent(ev);
    }
    /**
     * Two ways to realize the manual rotation of the rotation chart
     * 1,Using scrollTo and scrollBy to complete the manual rotation of the rotation chart
     * 1,Using the Scroller object to complete the manual effect of the rotation chart
     * @param event
     * @return
     */

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN://Indicates the moment the user presses
                stopAuto();//Stop picture rotation
                if (!scroller.isFinished()) {
                    scroller.abortAnimation();
                }
                isClick = true;
                x=(int)event.getX();
                break;
            case MotionEvent.ACTION_MOVE://Indicates the process of moving the user on the screen after pressing
                int moveX = (int) event.getX();
                int distance = moveX - x;
                scrollBy(-distance,0);
                x = moveX;
                isClick = false;
                break;
            case MotionEvent.ACTION_UP://It marks the moment when the user lifts up
                int scrollX = getScrollX();
                index = (scrollX + childwidth / 2) / childwidth;
                if (index < 0) {    //It's sliding to the far left
                    index = 0;
                } else if (index > children - 1) {//The description has slipped to the far right
                    index = children - 1;
                }

                if (isClick) {  //Click events
                    lister.chickImageIndex(index);
                } else {
                    int dx = index * childwidth - scrollX;
                    scroller.startScroll(scrollX,0,dx,0);
                    postInvalidate();
                    barnnerViewGroupLisnner.selectImage(index);
                }
                startAuto();//Turn on picture carousel
                break;
            default:
        }
        return true;
        //The purpose of returning true is to tell the parent View of the View Group container that we have handled the event
    }

    @Override
    protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
        if (b) {
            int lefrMargin = 0;
            for (int j = 0; j < children; j++) {
                View view = getChildAt(j);
                view.layout(lefrMargin,0,lefrMargin + childwidth,childheight);
                lefrMargin += childwidth;
            }
        }
    }

    public interface ImageBarnnerViewGroupLisnner{
        void selectImage(int index);
    }
}

2) New imagebannerframelayout class inherits from FrameLayout to implement two interfaces

public class ImageBannerFramLayout extends FrameLayout implements ImageBarnnerViewGroup.ImageBarnnerViewGroupLisnner,ImageBarnnerViewGroup.ImageBarnnerLister{

    private ImageBarnnerViewGroup imageBarnnerViewGroup;
    private LinearLayout linearLayout;

    private FramLayoutLisenner lisenner;

    public FramLayoutLisenner getLisenner() {
        return lisenner;
    }

    public void setLisenner(FramLayoutLisenner lisenner) {
        this.lisenner = lisenner;
    }

    public ImageBannerFramLayout(@NonNull Context context) {
        super(context);
        initImageBarnnerViewGroup();
        initDotLinearlayout();
    }



    public ImageBannerFramLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initImageBarnnerViewGroup();
        initDotLinearlayout();
    }

    public ImageBannerFramLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initImageBarnnerViewGroup();
        initDotLinearlayout();
    }

    public void addBitmaps(List<Bitmap> list) {
        for (int i = 0; i < list.size(); i++) {
            Bitmap bitmap = list.get(i);
            addBitmapToImageBarnnerViewGroup(bitmap);
            addDotToLinearlayout();
        }
    }

    private void addDotToLinearlayout() {
        ImageView iv = new ImageView(getContext());
        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams
                (LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.MATCH_PARENT);
        layoutParams.setMargins(5,5,5,5);
        iv.setLayoutParams(layoutParams);
        iv.setImageResource(R.drawable.dot_normal);
        linearLayout.addView(iv);
    }

    private void addBitmapToImageBarnnerViewGroup(Bitmap bitmap) {
        ImageView imageView = new ImageView(getContext());
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
        imageView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT));
        imageView.setImageBitmap(bitmap);
        imageBarnnerViewGroup.addView(imageView);
    }

    //Initialize the core class of custom image carousel function
    private void initImageBarnnerViewGroup() {
        imageBarnnerViewGroup = new ImageBarnnerViewGroup(getContext());
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams
                (FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
        imageBarnnerViewGroup.setLayoutParams(layoutParams);
        imageBarnnerViewGroup.setBarnnerViewGroupLisnner(this);//Pass Linsner to Framlayout
        imageBarnnerViewGroup.setLister(this);
        addView(imageBarnnerViewGroup);
    }

    //Initialize bottom dot layout
    private void initDotLinearlayout() {
        linearLayout = new LinearLayout(getContext());
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams
                (FrameLayout.LayoutParams.MATCH_PARENT, 40);
        linearLayout.setLayoutParams(layoutParams);
        linearLayout.setOrientation(LinearLayout.HORIZONTAL);
        linearLayout.setGravity(Gravity.CENTER);

        linearLayout.setBackgroundColor(Color.RED);
        addView(linearLayout);

        FrameLayout.LayoutParams layoutParams1 = (LayoutParams) linearLayout.getLayoutParams();
        layoutParams.gravity = Gravity.BOTTOM;
        linearLayout.setLayoutParams(layoutParams1);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            linearLayout.setAlpha(0.5f);
        } else {
            linearLayout.getBackground().setAlpha(100);
        }
    }


    @Override
    public void selectImage(int index) {
        int count = linearLayout.getChildCount();
        for (int i = 0;i < count; i++) {
            ImageView iv = (ImageView) linearLayout.getChildAt(i);
            if (i == index) {
                iv.setImageResource(R.drawable.dot_select);
            } else {
                iv.setImageResource(R.drawable.dot_normal);
            }
        }
    }

    @Override
    public void chickImageIndex(int pos) {
        lisenner.chickImageIndex(pos);
    }

    public interface FramLayoutLisenner{
        void chickImageIndex(int pos);
    }
}

3. Activity > main

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <com.example.tony.imagegroup.view.ImageBannerFramLayout
        android:id="@+id/image_group"
        android:layout_width="match_parent"
        android:layout_height="200dp">
    </com.example.tony.imagegroup.view.ImageBannerFramLayout>
</RelativeLayout>

4. Create two new drawable resource files, dot_normal.xml and dot_select.xml, to realize the small dots at the bottom of the rotation chart

The difference is that the former is white and the latter is black

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="@android:color/white"/>
    <size android:height="10dp"
        android:width="10dp"/>
</shape>

3, Operation results

Topics: Android xml encoding