可以看到这是一个轮播图,只不过我们以前在用的时候一般都是用ViewPager,用正常的ViewPager有几点坏处:
- 不能无限循环
- 不能控制自动播放
所以这就需要自己封装一个了,其实还是用的ViewPager,只不过更改了他的触摸事件并且设置了自动轮播的时间
自定义BannerView控件
下面开始说如何写
首先我们要确定一下这个banner有几个需要注意的点:
无限轮播
点击事件
指示器
指示器的滑动效果
需要自定义banner的布局
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="250dp" />
<LinearLayout
android:id="@+id/layout_dot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:gravity="center"
android:orientation="horizontal"
android:paddingBottom="10dp" />
</FrameLayout>
还需要在values文件夹下创建自定义view的属性文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="GramophoneView">
<!--图片半径,不是整个唱片的半径,只是图片的半径-->
<attr name="picture_radius" format="dimension" />
<!--图片资源id-->
<attr name="src" format="reference" />
<!--唱片旋转速度,实际上在Java代码里面被当做唱片旋转时每次变化的角度-->
<attr name="disk_rotate_speed" format="float" />
</declare-styleable>
<!--banner的属性设置-->
<declare-styleable name="Banner">
<attr name="size" format="dimension" />
<attr name="margin" format="dimension" />
</declare-styleable>
</resources>
小圆点的选中图片(这里使用shape绘制图片)
<?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/holo_green_dark"></solid>
<size
android:width="10dp"
android:height="10dp" />
</shape>
小圆点的未选中状态
<?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"></solid>
<size
android:width="10dp"
android:height="10dp" />
</shape>
好了,开始自定义banner
public class AuToBanner extends RelativeLayout implements ViewPager.OnPageChangeListener {
private final int DELAY_TIME = 4000;//自动轮播时间
private List<String> mUrls;
private List<ImageView> mViewpagerViews;
private List<ImageView> mDotImageviews;
private Context context;
private int size;//圆点的大小
private int margin;//圆点的间距
private int count;//viewpager中view的数量
private ViewPager mViewPager;
private LinearLayout mDotlayout;//圆点布局
private int currentItem;//当前viewpager索引
private Handler handler = new Handler();
private BannerClicklistener mBannerClicklistener;
private MyPager myPager;
public AuToBanner(Context context) {
this(context, null);
}
public AuToBanner(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AuToBanner(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
/**
* 初始化数据
*
* @param context
* @param attrs
*/
private void init(Context context, AttributeSet attrs) {
this.context = context;
mUrls = new ArrayList<>();
mViewpagerViews = new ArrayList<>();
mDotImageviews = new ArrayList<>();
//拿到自定义的属性数组
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.Banner);
//得到数组里的自定义的size(圆点大小)
size = typedArray.getDimensionPixelSize(R.styleable.Banner_size, 10);
//得到数组里的自定义的margin(圆点间距)
margin = typedArray.getDimensionPixelSize(R.styleable.Banner_margin, 10);
typedArray.recycle();//通知jvm的垃圾回收器,当你回收对象的时候,一定要把我回收了
View view = LayoutInflater.from(context).inflate(R.layout.auto_banner_view, this, true);
mViewPager = view.findViewById(R.id.viewpager);
mDotlayout = view.findViewById(R.id.layout_dot);
//添加viewpager页面改变监听
mViewPager.addOnPageChangeListener(this);
}
/**
* 绘制自定义view的所有元素
*/
public void display() {
//绘制viewpager
drawViewpager();
//绘制圆点
drawDots();
//设置自动滚动
setAuto();
}
/**
* 设置自动滚动
*/
private void setAuto() {
handler.postDelayed(mTask, DELAY_TIME);
}
/**
* 定时任务
*/
Runnable mTask = new Runnable() {
@Override
public void run() {
currentItem++;
if (currentItem >= count) {
currentItem = 0;
}
mViewPager.setCurrentItem(currentItem);
handler.postDelayed(this, DELAY_TIME);
}
};
/**
* 传urls
*
* @param urls
*/
public AuToBanner loadData(List<String> urls) {
this.mUrls = urls;
this.count = urls.size();
return this;
}
/**
* 绘制圆点
*/
private void drawDots() {
for (int i = 0; i < count; i++) {
ImageView iv = new ImageView(context);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(size, size);
params.leftMargin = margin;
params.rightMargin = margin;
iv.setLayoutParams(params);
mDotImageviews.add(iv);
if (i == 0) {
iv.setImageResource(R.drawable.dot_selected);
} else {
iv.setImageResource(R.drawable.dot_normal);
}
mDotlayout.addView(iv);
}
}
private void drawViewpager() {
for (int i = 0; i < count; i++) {
ImageView iv = new ImageView(context);
iv.setScaleType(ImageView.ScaleType.CENTER_CROP);
mViewpagerViews.add(iv);
}
if (mViewpagerViews != null) {
myPager = new MyPager();
mViewPager.setAdapter(myPager);
}
}
class MyPager extends PagerAdapter {
@Override
public int getCount() {
return mViewpagerViews.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, final int position) {
ImageView view = mViewpagerViews.get(position);
container.addView(view);
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
//实现点击逻辑
mBannerClicklistener.onClickListener(position);
}
});
ImageLoader.getInstance().displayImage(mUrls.get(position), view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
/**
* 取消自动轮播任务
*/
private void stopAuto() {
handler.removeCallbacks(mTask);//取消任务
}
/**
* 供外部调用者调用的接口类
*/
public interface BannerClicklistener {
void onClickListener(int pos);
}
/**
* 供外部调用者初始化接口对象
*/
public void setBannerClicklistener(BannerClicklistener bannerClicklistener) {
this.mBannerClicklistener = bannerClicklistener;
}
//取消轮播任务
public void cancel() {
handler.removeCallbacks(mTask);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
/**
* 切换页面的监听
*
* @param position
*/
@Override
public void onPageSelected(int position) {
for (int i = 0; i < count; i++) {
if (i == position) {
mDotImageviews.get(i).setImageResource(R.drawable.dot_selected);
} else {
mDotImageviews.get(i).setImageResource(R.drawable.dot_normal);
}
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
}
代码的动人之处在于可以自己说话,只需注意几点,
1. ViewPager滑动是没有延时的,谷歌也没有提供具体的接口去实现延时滑动。所以这里使用反射去重新设置ViewPager的滑动世间。
2. 无限轮播可用线程池,定时器,Handler等等实现,这里采用最简单的Handler实现。首先自定义一个轮播任务,实现Runnable接口,在run方法里使用Handler发送延时消息来不停轮播,并且提供start方法和stop方法开启和停止轮播。在初始化视图完毕时,开启轮播。
开始在activity中或者fragment中使用了
urls = new ArrayList<>();
urls.add("http://pic71.nipic.com/file/20150709/9885883_133323978000_2.jpg");
urls.add("http://image.club.china.com/twhb/2014/8/24/1011/1408883429066.jpg");
urls.add("http://photocdn.sohu.com/20130115/Img363518686.jpg");
urls.add("http://image.club.china.com/twhb/2016/1/14/1013/1452772291776_495.jpg");
urls.add("http://images.china.cn/attachement/jpg/site1000/20150827/001fd04cf0161748f83d1c.jpg");
urls.add("http://image.club.china.com/twhb/2016/2/3/1013/1454513937918.jpeg");
//解耦
banner.loadData(urls).display();//构建者模式返回对象本身
banner.setBannerClicklistener(new AuToBanner.BannerClicklistener() {
@Override
public void onClickListener(int pos) {
Toast.makeText(getActivity(), "" + pos, Toast.LENGTH_SHORT).show();
}
});
以上就是自定义banner的全部使用了,不过,我觉得真扯,xbanner比这个好用多了!
奉献一篇xbanner的使用讲解:Android-框架之 -XBanner使用
GitHub XBanner官网地址
京东购物车自定义加减器
整个控件其实是由两个Button和一个EditText组成,直接上代码进行分析。初始化控件,设置了自定义属性(这几个自定义属性的作用大概通过名字也能够知道了)和设置监听器
layout文件夹下创建view_amount (自定义加减器布局)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_amount_layout"
android:divider="@drawable/divider"
android:focusable="true"
android:orientation="horizontal"
android:showDividers="middle">
<Button
android:id="@+id/btnDecrease"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/btn_amount"
android:gravity="center"
android:text="-" />
<EditText
android:id="@+id/etAmount"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
android:background="@null"
android:focusable="false"
android:gravity="center"
android:inputType="number"
android:minWidth="60dp"
android:text="1" />
<Button
android:id="@+id/btnIncrease"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/btn_amount"
android:gravity="center"
android:text="+" />
</LinearLayout>
attr.xml中设置加减器属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--加减器属性-->
<declare-styleable name="AmountView">
<!-- 左右2边+-按钮的宽度 -->
<attr name="btnWidth" format="dimension" />
<!-- 中间TextView的宽度 -->
<attr name="tvWidth" format="dimension" />
<!--<attr name="tvColor" format="color"/>-->
<attr name="tvTextSize" format="dimension"/>
<attr name="btnTextSize" format="dimension"/>
</declare-styleable>
</resources>
drawable文件夹下
1.bg_amount_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFFFFF" />
<stroke
android:width="1dp"
android:color="@color/divider" />
<padding
android:bottom="1dp"
android:left="1dp"
android:right="1dp"
android:top="1dp" />
</shape>
2.btn_amount.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@color/divider" />
<item android:state_enabled="false" android:drawable="@color/divider" />
<item android:drawable="@android:color/white" />
</selector>
3.divider.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
android:width="0.5dp"/>
<solid android:color="@color/divider"/>
</shape>
4.colors.xml,就一个颜色
<color name="divider">#ffd2d2d2</color>
自定义组合加减器
public class AmountView extends LinearLayout implements View.OnClickListener, TextWatcher {
private static final String TAG = "AmountView";
private int amount = 1; //购买数量
private int goods_storage = 1; //商品库存
private OnAmountChangeListener mListener;
private EditText etAmount;
private Button btnDecrease;
private Button btnIncrease;
public AmountView(Context context) {
this(context, null);
}
public AmountView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.view_amount, this);
etAmount = (EditText) findViewById(R.id.etAmount);
btnDecrease = (Button) findViewById(R.id.btnDecrease);
btnIncrease = (Button) findViewById(R.id.btnIncrease);
btnDecrease.setOnClickListener(this);
btnIncrease.setOnClickListener(this);
etAmount.addTextChangedListener(this);
TypedArray obtainStyledAttributes = getContext().obtainStyledAttributes(attrs, R.styleable.AmountView);
int btnWidth = obtainStyledAttributes.getDimensionPixelSize(R.styleable.AmountView_btnWidth, LayoutParams.WRAP_CONTENT);
int tvWidth = obtainStyledAttributes.getDimensionPixelSize(R.styleable.AmountView_tvWidth, 80);
int tvTextSize = obtainStyledAttributes.getDimensionPixelSize(R.styleable.AmountView_tvTextSize, 0);
int btnTextSize = obtainStyledAttributes.getDimensionPixelSize(R.styleable.AmountView_btnTextSize, 0);
obtainStyledAttributes.recycle();
LayoutParams btnParams = new LayoutParams(btnWidth, LayoutParams.MATCH_PARENT);
btnDecrease.setLayoutParams(btnParams);
btnIncrease.setLayoutParams(btnParams);
if (btnTextSize != 0) {
btnDecrease.setTextSize(TypedValue.COMPLEX_UNIT_PX, btnTextSize);
btnIncrease.setTextSize(TypedValue.COMPLEX_UNIT_PX, btnTextSize);
}
LayoutParams textParams = new LayoutParams(tvWidth, LayoutParams.MATCH_PARENT);
etAmount.setLayoutParams(textParams);
if (tvTextSize != 0) {
etAmount.setTextSize(tvTextSize);
}
}
public void setOnAmountChangeListener(OnAmountChangeListener onAmountChangeListener) {
this.mListener = onAmountChangeListener;
}
public void setGoods_storage(int goods_storage) {
this.goods_storage = goods_storage;
}
@Override
public void onClick(View v) {
int i = v.getId();
if (i == R.id.btnDecrease) {
if (amount > 1) {
amount--;
etAmount.setText(amount + "");
}
} else if (i == R.id.btnIncrease) {
if (amount < goods_storage) {
amount++;
etAmount.setText(amount + "");
}
}
etAmount.clearFocus();
if (mListener != null) {
mListener.onAmountChange(this, amount);
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if (s.toString().isEmpty())
return;
amount = Integer.valueOf(s.toString());
if (amount > goods_storage) {
etAmount.setText(goods_storage + "");
return;
}
if (mListener != null) {
mListener.onAmountChange(this, amount);
}
}
public interface OnAmountChangeListener {
void onAmountChange(View view, int amount);
}
}
在activity中或者fragment中使用
amountView.setGoods_storage(50);//设置最大数量
amountView.setOnAmountChangeListener(new AmountView.OnAmountChangeListener() {
@Override
public void onAmountChange(View view, int amount) {
Toast.makeText(getActivity(), "Amount=> " + amount, Toast.LENGTH_SHORT).show();
}
});