自定义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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值