自定義Banner廣告條

自定義Banner廣告條

我們在APP上都見過廣告條,幾張圖片輪播,今天,我們就自定義一個廣告條View,先來看看效果(gif轉出來效果可能不太好)

在這裏插入圖片描述

首先,我們先說一下,這個自定義View(起名爲BannerView)是由哪些控件組成的。

顯示圖片並且一直輪播的使用的是ViewPager,照片上一個灰色的半透明是一個LinearLayout,裏面包含TextView顯示文字,還有一個LinearLayout,負責顯示原點,表示當前是第幾張圖片。

看我們的佈局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!--負責顯示圖片-->
    <androidx.viewpager.widget.ViewPager
        android:id="@+id/viewpager_main"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <!--顯示文字和圓點-->
    <LinearLayout
        android:id="@+id/ll_status_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@id/viewpager_main"
        android:background="#44000000"
        android:orientation="vertical"
        android:visibility="gone">
        <!--文字-->
        <TextView
            android:id="@+id/tv_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="2dp"
            android:textColor="#ffffff" />
        <!--圓點-->
        <LinearLayout
            android:id="@+id/ll_point_group"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="horizontal"
            android:padding="5dp" />
    </LinearLayout>
</RelativeLayout>

佈局文件已經寫好,下一步就開始擼代碼啦

新建一個class文件FxBannerView(名字隨便取),繼承RelativeLayout,因爲我們佈局文件的根佈局使用的是RelativeLayout,然後重寫三個構造方法,這是每一個自定義控件都必須要做的

public FxBannerView(Context context) {
    this(context, null);
    mContext = context;
}

public FxBannerView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
    mContext = context;

}

public FxBannerView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    mContext = context;
}

接下來,我們要完成PageView的無限滾動,圓點的創建及顏色變化,還有文字隨圖片變化而變化

  1. 首先,我們需要設置數據來源,既然是一張圖片配一行文字,那就使用LinkedHashMap<int,String> mData,然後解析出圖片集合imgs和文字集合strings
private void initData() {
    imgs.clear();
    strings.clear();
    for (Object o : mData.entrySet()) {
        Map.Entry entry = (Map.Entry) o;
        imgs.add((int) entry.getKey());
        strings.add((String) entry.getValue());
    }
}
  1. 然後,我們獲取佈局文件的控件
private void initView(Context context) {
		View root = LayoutInflater.from(context).inflate(R.layout.banner_view, this);
		mLlPointGroup = root.findViewById(R.id.ll_point_group);//圓點集合
		mTvName = root.findViewById(R.id.tv_name);//文字顯示
		mViewpagerMain = root.findViewById(R.id.viewpager_main);//viewpager顯示圖片輪播
  ...
}
  1. 將數據傳給控件
for (int i = 0; i < imgs.size(); i++) {
            int imgSrc = imgs.get(i);
//            添加圖
            ImageView imageView = new ImageView(context);
            imageView.setBackgroundResource(imgSrc);
            imageViews.add(imageView);
//            添加點
            ImageView pointView = new ImageView(context);
            pointView.setBackgroundResource(R.drawable.point_seletor);
//            px轉dp
            float scale = getResources().getDisplayMetrics().density;
            int width = (int) (scale * 8 + 0.5f);
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(width, width);
            params.rightMargin = width;
            pointView.setLayoutParams(params);
            mLlPointGroup.addView(pointView);
            if (i == 0) {
                pointView.setEnabled(true);
            } else {
                pointView.setEnabled(false);
            }
        }

繪製點使用的背景資源使用select和shape,使用shape畫出兩個圓,一個灰色一個紅色,表示選中和非選中,然後select根據enable屬性決定他是紅色還是灰色

point_seletor.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/point_disable" android:state_enabled="false" />
    <item android:drawable="@drawable/point_enable" android:state_enabled="true" />
</selector>

point_disable.xml

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

point_enable.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <size
        android:width="8dp"
        android:height="8dp" />
    <solid android:color="#FF0000" />
</shape>
  1. 我們已經拿到了包含ImageView集合了,下一步就是將集合傳給ViewPager,使用過ViewPager的小夥伴一定知道ViewPager需要和PagerAdapter一起使用,自定義PagerAdapter在我另一篇博客中有說明
  2. 我們將imageView集合傳給adapter
myAdapter.setImageViews(imageViews);
mViewpagerMain.setAdapter(myAdapter);
myAdapter.notifyDataSetChanged();
  1. pageView數據設置完後,我們設置TextView,
 mTvName.setText(strings.get(prePosition));

我們的自定義BannerView就完成了

完整代碼

package com.felix.baselibrary.UI.banner;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import androidx.viewpager.widget.ViewPager;

import com.felix.baselibrary.R;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 廣告條
 * 必須使用setData方法將數據傳入,否則不能顯示
 */
public class FxBannerView extends RelativeLayout {
    // TODO: 2020/4/20 數據來源可配置
    private ViewPager mViewpagerMain;
    private TextView mTvName;
    private LinearLayout mLlPointGroup;


    private LinkedHashMap<Integer, String> mData = new LinkedHashMap<>();
    private ArrayList<Integer> imgs = new ArrayList<>();
    private ArrayList<String> strings = new ArrayList<>();
    private ArrayList<ImageView> imageViews;
    private final static String TAG = FxBannerView.class.getSimpleName();
    private int prePosition = 0;

    private boolean isCycle = true;
    private long mDelayMillis = 4000;
    private FxBannerAdapter myAdapter;
    private Context mContext;
    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            mViewpagerMain.setCurrentItem(mViewpagerMain.getCurrentItem() + 1);
            mHandler.sendEmptyMessageDelayed(0, mDelayMillis);
            return true;
        }
    });
    private LinearLayout mLlStatusBar;
    private onBannerItemClickListener mItemClickListener;

    /**
     * @param adapter BannerAdapter
     * @param map     KV結構map,HashMap<Integer, String>
     *                Integer:圖片的標識,R.drawable.xxx
     *                String:圖片描述
     */
    public void setAdapter(FxBannerAdapter adapter, LinkedHashMap<Integer, String> map) {
        myAdapter = new FxBannerAdapter();
        mData = map;
        initData();
        initView(mContext);
    }

    public void setData(LinkedHashMap<Integer, String> map) {
        mData = map;
        initData();
        initView(mContext);
    }

    public void setOnBannerItemClickListener(onBannerItemClickListener listener) {
        mItemClickListener = listener;
        if (mItemClickListener != null) {
            myAdapter.setOnItemClickListener(new FxBannerAdapter.OnAdapterItemClickListener() {
                @Override
                public void onItemClick(View v) {
                    mItemClickListener.onItemClick(v);
                    Log.d("BannerView", "onClick");
                }
            });
        }
    }

    /**
     * 設置是否自動輪播
     *
     * @param b true or false
     * @hide
     */
    private void setAutoRotation(boolean b) {
        isCycle = b;
    }

    private void setAutoRotationDuration(int mills) {
        mDelayMillis = mills;
    }

        public FxBannerView(Context context) {
            this(context, null);
            mContext = context;
        }

        public FxBannerView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
            mContext = context;

        }

        public FxBannerView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            mContext = context;
        }

    private void initData() {
        imgs.clear();
        strings.clear();
        for (Object o : mData.entrySet()) {
            Map.Entry entry = (Map.Entry) o;
            imgs.add((int) entry.getKey());
            strings.add((String) entry.getValue());
        }
    }

    private void initView(Context context) {
        View root = LayoutInflater.from(context).inflate(R.layout.banner_view, this);
        mLlPointGroup = root.findViewById(R.id.ll_point_group);
        mLlStatusBar = root.findViewById(R.id.ll_status_bar);
        mTvName = root.findViewById(R.id.tv_name);
        mViewpagerMain = root.findViewById(R.id.viewpager_main);
        imageViews = new ArrayList<>();
        if (myAdapter == null) {
            return;
        }
        mLlStatusBar.setVisibility(VISIBLE);
        mLlPointGroup.removeAllViews();
        for (int i = 0; i < imgs.size(); i++) {
            int imgSrc = imgs.get(i);
//            添加圖
            ImageView imageView = new ImageView(context);
            imageView.setBackgroundResource(imgSrc);
            imageViews.add(imageView);
//            添加點
            ImageView pointView = new ImageView(context);
            pointView.setBackgroundResource(R.drawable.point_seletor);
//            px轉dp
            float scale = getResources().getDisplayMetrics().density;
            int width = (int) (scale * 8 + 0.5f);
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(width, width);
            params.rightMargin = width;
            pointView.setLayoutParams(params);
            mLlPointGroup.addView(pointView);
            if (i == 0) {
                pointView.setEnabled(true);
            } else {
                pointView.setEnabled(false);
            }
        }
        if (imageViews.isEmpty()) {
            return;
        }
        myAdapter.setImageViews(imageViews);
        mViewpagerMain.setAdapter(myAdapter);
        myAdapter.notifyDataSetChanged();
        mViewpagerMain.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                int realPosition = position % imageViews.size();
                mTvName.setText(strings.get(realPosition));
                mLlPointGroup.getChildAt(prePosition).setEnabled(false);
                mLlPointGroup.getChildAt(realPosition).setEnabled(true);
                prePosition = realPosition;

            }

            /**
             當頁面滾動狀態變化的時候回調這個方法
             靜止->滑動
             滑動-->靜止
             靜止-->拖拽
             */
            @Override
            public void onPageScrollStateChanged(int state) {
                if (state == ViewPager.SCROLL_STATE_DRAGGING) {
                    isCycle = true;
                    mHandler.removeCallbacksAndMessages(null);
                } else if (state == ViewPager.SCROLL_STATE_IDLE && isCycle) {
                    isCycle = false;
                    mHandler.removeCallbacksAndMessages(null);
                    mHandler.sendEmptyMessageDelayed(0, mDelayMillis);
                }
            }
        });

        int midItem = Integer.MAX_VALUE / 2 - Integer.MAX_VALUE / 2 % imageViews.size();
        mViewpagerMain.setCurrentItem(midItem);
        mTvName.setText(strings.get(prePosition));
        mHandler.sendEmptyMessageDelayed(0, mDelayMillis);
    }

    public interface onBannerItemClickListener {
        void onItemClick(View v);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章