教你打造广告位的循环播放

转载请标明出处:
http://blog.csdn.net/hai_qing_xu_kong/article/details/50858492
本文出自:【顾林海的博客】

前言

最近刚把驾校的科目一考完,也有充足的时间去研究android,目前自己的打算是这样的,先从自定义控件开始一步一步的学习研究,之后会去写一些设计模式之类的文章,到时候欢迎大家来浏览。
‘广告位’这个词汇大家一定不陌生,现在大部分的APP都会有广告位,打开某款APP,首页顶部就会出现一直自动播放的广告,这种效果还是挺炫的,那如何实现这个功能呢,这篇文章将会揭晓广告位的神秘面纱。

今天实现的效果是这样的:

打造广告位的前奏

在开始敲代码前,先来看看这个广告位到底应该怎样实现,从效果图看我们发现广告位有以下特点:

  • 广告位可以自动轮播的。
  • 广告位是可以向右无限轮播的。
  • 广告位底部有一个指示器(三个小圆圈),指示器内的圆点个数是按图片的个数来设定的。
  • 切换到哪个图片,底下的指示器也切到相应的位置。

广告位可以自动轮播的

广告位可以自动轮播的,自动轮播意味着我们需要一个定时器,如何实现定时器,有三种方式:

  1. Handler+Thread
  2. Handler类自带的postDelyed
  3. Handler+Timer+TimerTask

这三种方式效果都一样,我用的是第二种:Handler类自带的postDelyed

private Handler handler = new Handler();
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            // 开启轮播
            switchItem();
            handler.postDelayed(this, 1000);
        }
};
/** * 开启轮播 */
public void start() {
    handler.postDelayed(runnable, 2000);
}

/** * 关闭轮播 */
public void stop() {
    handler.removeCallbacks(runnable);
}

代码非常的easy,间隔1秒的轮播。

广告位是可以向右无限轮播的

这里面实现广告位的控件,我是选用Gallery控件,通过为Gallery绑定相应的适配器,我们就能通过手指来回切换图片,那如何无限向右轮播的呢?Gallery通过绑定适配器,自定义适配器时,我们要创建一个继承BaseAdapter的类,重写里面的几个方法,看到有个getCount方法,它主要要来告诉Gallery有多少个Item,这时可以这样改动:

        @Override
        public int getCount() {
            final int count = mAdvertisementObjectList == null ? 0
                    : mAdvertisementObjectList.size();
            return count > 1 ? Integer.MAX_VALUE : count;
        }

将显示的Item数改为Integer.MAX_VALUE。

广告位指示器实现

从效果图能看到底部居中有个指示器,指示器的圆点个数是可控,这里是通过自定义View来实现的,通过绘制图片数的圆点。具体代码如下:

package com.example.advertisementproject.view;

import com.example.advertisementproject.R;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout.LayoutParams;

/** * 指示器 * * @author Linhai Gu */
public class IndicatorView extends View {

    private LayoutParams mParams;
    /** * 选中的图标 */
    private Bitmap mSelectorIcon;
    /** * 未选中的图标 */
    private Bitmap mUnselectorIcon;
    /** * 画笔 */
    private Paint mPaint;
    /** * 图标个数 */
    private int mCount;

    /** * 当前高亮显示圆点的位置 */
    private int mSelectedIndex;

    /** * 圆点的宽度 */
    private int mIconWidth;
    /** * 圆点的高度 */
    private int mIconHeight;

    /** * 指示器的宽度 */
    private int mWidth;

    /** * 指示器的高度 */
    private int mHeight;

    /** * 圆点间的空隔 */
    private int mSpace;

    public IndicatorView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public IndicatorView(Context context) {
        super(context);
        init(context);
    }

    private void init(Context _context) {
        /** * 创建画笔 */
        mPaint = new Paint();
        /** * 获取选中图标的bitmap */
        mSelectorIcon = BitmapFactory.decodeResource(_context.getResources(),
                R.drawable.selector_point);
        /** * 获取未选中图标的bitmap */
        mUnselectorIcon = BitmapFactory.decodeResource(_context.getResources(),
                R.drawable.unselector_point);
        mIconWidth = mUnselectorIcon.getWidth();
        mIconHeight = mUnselectorIcon.getHeight();
        mParams = new LayoutParams(LayoutParams.WRAP_CONTENT, mIconHeight);
        setLayoutParams(mParams);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawSelectIcon(mSelectedIndex, canvas);
    }

    /** * 绘制图标 * * @param _select * @param _canvas */
    private void drawSelectIcon(int _select, Canvas _canvas) {
        for (int i = 0; i < mCount; i++) {
            if (i == _select) {
                /** * 被选中的当前显示的指示器圆点 */
                drawSelectedIcon(i, _canvas);
            } else {
                /** * 未选中时的指示器圆点 */
                drawUnselectedIcon(i, _canvas);
            }
        }
    }

    /** * 绘制选中时的圆点 * * @param _index * @param _canvas */
    private void drawSelectedIcon(int _index, Canvas _canvas) {
        _canvas.drawBitmap(mSelectorIcon, _index * (mIconWidth + mSpace), 0,
                mPaint);
    }

    /** * 绘制未选中时的圆点 * * @param _index * @param _canvas */
    private void drawUnselectedIcon(int _index, Canvas _canvas) {
        _canvas.drawBitmap(mUnselectorIcon, _index * (mIconWidth + mSpace), 0,
                mPaint);
    }

    /** * 指示器圆点的总个数 * * @param _count * 总数 * */
    public void setTotal(int _count) {
        this.mCount = _count;
        this.setLayoutParams(mParams);
        if (_count == 0) {
            this.setVisibility(View.INVISIBLE);
        } else {
            mWidth = mIconWidth * _count + (_count - 1) * mSpace;
            mHeight = mIconWidth;
            this.setVisibility(View.VISIBLE);
        }
    }

    /** * 选中的圆点的位置 * * @param _selected * 高亮圆点位置 */
    public void setSelected(int _selected) {
        mSelectedIndex = _selected;
        invalidate();
    }

    /** * 设置圆点间的空隔 * * @param _space */
    public void setSpace(int _space) {
        this.mSpace = _space;
    }

    /** * 获取指示器宽度 * * @return */
    public int getIndicatorWidth() {
        return mWidth;
    }

}

绘制圆点时,我们要把握好绘制的位置,drawBitmap有以下几个参数:

public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) 

bitmap是我们要绘制的圆点
left是距离屏幕左边的距离
top是距离顶部的距离
paint是我们定义的画笔

圆点与圆点有空隔,如果有两个图片的话,就有一个空隔,三个图片的话就有两个空隔,因此整个指示器的宽度就可以计算出来:指示器宽度=圆点自身宽度*圆点总数+(圆点总数-1)间隔的距离。

mWidth = mIconWidth * _count + (_count - 1) * mSpace;

怎样确定绘制圆点的位置,比如要绘制第一个位置的圆点,这时距离左边距离应该是(这里的起始位置从0开始): 0*(绘制圆点的宽度+空隔)=0,也就是在0,0位置绘制;如果是绘制第二个位置:1*(绘制圆点的宽度+空隔),这样依次类推,两圆点绘制的距离是圆点宽度加上空隔的距离。

    /** * 绘制选中时的圆点 * * @param _index * @param _canvas */
    private void drawSelectedIcon(int _index, Canvas _canvas) {
        _canvas.drawBitmap(mSelectorIcon, _index * (mIconWidth + mSpace), 0,
                mPaint);
    }

    /** * 绘制未选中时的圆点 * * @param _index * @param _canvas */
    private void drawUnselectedIcon(int _index, Canvas _canvas) {
        _canvas.drawBitmap(mUnselectorIcon, _index * (mIconWidth + mSpace), 0,
                mPaint);
    }

(正餐)打造广告位

创建我们的AdvertisementView并继承RelativeLayout,在这里加载我们的广告位的布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/rl_group" android:layout_width="match_parent" android:layout_height="wrap_content" >

    <Gallery  android:id="@+id/gl_images" android:layout_width="wrap_content" android:layout_height="wrap_content" />

    <LinearLayout  android:id="@+id/ll_group" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginBottom="4dp" android:orientation="horizontal" >

        <com.example.advertisementproject.view.IndicatorView  android:id="@+id/view_indicator" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" >
        </com.example.advertisementproject.view.IndicatorView>

    </LinearLayout>

</RelativeLayout>

布局的效果就是将指示器放在底部,居中操作我们放在代码中实现,这时因为我们要获取指示器的宽度,通过屏幕宽度/2-(指示器宽度/2)得到指示器放置的起始位置。

整体的代码比较简单,一下是整个广告位轮播的源码:

package com.example.advertisementproject.view;

import java.util.ArrayList;

import com.example.advertisementproject.R;
import com.example.advertisementproject.view.entity.AdvertisementObject;

import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

/** * 广告位轮播 * * @author Linhai Gu * */
public class AdvertisementView extends RelativeLayout implements OnItemClickListener, OnItemSelectedListener {

    private Context mContext;

    public final int ADVER_HEIGHT = 50;

    /** * 指示器容器 */
    private LinearLayout ll_group;
    /** * 指示器 */
    private IndicatorView mIndicatorView;
    private RelativeLayout rl_group;

    /** * 广告图宽度 */
    private float mWidth;

    /** * 广告图的高度 */
    private int mHeight;

    private Gallery mGallery;

    private ImageSwitcherAdapter mImageSwitcherAdapter;

    /** * 数据源 */
    private ArrayList<AdvertisementObject> mAdvertisementObjectList = new ArrayList<AdvertisementObject>();
    /** * 是不是本地资源ID */
    private boolean isResourceId;
    private int mMax;

    private OnItemClickListener mOnItemClickListener;

    public AdvertisementView(Context context) {
        this(context, null);
    }

    public AdvertisementView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public AdvertisementView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.mContext = context;
        initData();
        initView();
        initAdapter();
        initEvent();
    }

    /** * 数据初始化 */
    private void initData() {
        WindowManager windowManager = (WindowManager) mContext
                .getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics metric = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(metric);
        mWidth = metric.widthPixels;
    }

    /** * 初始化View */
    private void initView() {
        View rootView = LayoutInflater.from(mContext).inflate(
                R.layout.advertisement_view_layout, this);
        ll_group = (LinearLayout) rootView.findViewById(R.id.ll_group);
        mGallery = (Gallery) rootView.findViewById(R.id.gl_images);
        rl_group = (RelativeLayout) rootView.findViewById(R.id.rl_group);
        mIndicatorView = (IndicatorView) rootView
                .findViewById(R.id.view_indicator);
        /* * 设置圆点间隔距离 */
        mIndicatorView.setSpace(6);
        LayoutParams params = new LayoutParams(new LayoutParams(
                RelativeLayout.LayoutParams.MATCH_PARENT, mHeight));
        this.setLayoutParams(params);
    }

    private void setLayoutParams() {
        LayoutParams params = new LayoutParams(new LayoutParams(
                RelativeLayout.LayoutParams.MATCH_PARENT, mHeight));
        rl_group.setLayoutParams(params);
    }

    /** * 给广告空间绑定适配器 */
    private void initAdapter() {
        mImageSwitcherAdapter = new ImageSwitcherAdapter();
        mGallery.setAdapter(mImageSwitcherAdapter);
    }

    private void initEvent() {
        mGallery.setOnItemClickListener(this);
        mGallery.setOnItemSelectedListener(this);
    }

    /** * 设置图片大小 * * @param imageView */
    private void setPxImage(ImageView imageView) {
        android.view.ViewGroup.LayoutParams lp;
        lp = imageView.getLayoutParams();
        lp.height = mHeight;
        lp.width = (int) mWidth;
        imageView.setLayoutParams(lp);
    }

    public class ImageSwitcherAdapter extends BaseAdapter {

        @Override
        public int getCount() {
            final int count = mAdvertisementObjectList == null ? 0
                    : mAdvertisementObjectList.size();
            return count > 1 ? Integer.MAX_VALUE : count;
        }

        @Override
        public Object getItem(int position) {
            mAdvertisementObjectList.get(position % mMax);
            return null;
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder;
            if (convertView == null) {
                viewHolder = new ViewHolder();
                convertView = LayoutInflater.from(mContext).inflate(
                        R.layout.advertisement_item_layout, null);
                viewHolder.iv_image = (ImageView) convertView
                        .findViewById(R.id.iv_image);
                convertView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }
            AdvertisementObject advertisementObject = mAdvertisementObjectList
                    .get(position % mMax);
            setPxImage(viewHolder.iv_image);
            if (isResourceId) {
                /** * 从本地加载 */
                viewHolder.iv_image
                        .setImageResource(advertisementObject.mResourceId);
            } else {
                /** * 从网络加载 */

            }

            return convertView;
        }
    }

    static class ViewHolder {
        ImageView iv_image;
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position,
            long id) {
        mOnItemClickListener.onItemClick(position % mMax);
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position,
            long id) {
        Log.i("TAG", "position:" + position);
        mIndicatorView.setSelected(position % mMax);
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {

    }

    /** * 设置指示器位置 */
    private void setIndicatorParams() {
        LayoutParams params = (LayoutParams) ll_group.getLayoutParams();
        params.leftMargin = (int) (mWidth / 2 - mIndicatorView.getWidth() / 2);
        ll_group.setLayoutParams(params);
    }

    public interface OnItemClickListener {
        void onItemClick(int position);
    }

    protected void switchItem() {
        mGallery.onScroll(null, null, 1, 0);
        mGallery.onKeyDown(KeyEvent.KEYCODE_DPAD_RIGHT, null);
    }

    private Handler handler = new Handler();
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            // 开启轮播
            switchItem();
            handler.postDelayed(this, 1000);
        }
    };

    // ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    // ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    /** * 设置数据 如果是图片的URL的话,_isResourceId为false * * @param data * @param _isResourceId * true:本地图片资源ID false:网络图片URL */
    public void setData(ArrayList<AdvertisementObject> _data,
            boolean _isResourceId) {
        this.mAdvertisementObjectList = _data;
        this.isResourceId = _isResourceId;
        mMax = mAdvertisementObjectList == null ? 0 : mAdvertisementObjectList
                .size();
        // 设置指示器
        mIndicatorView.setTotal(mMax);
        setIndicatorParams();
        mImageSwitcherAdapter.notifyDataSetChanged();
    }

    /** * 设置广告图的宽高比例 * * @param width * @param height */
    public void setAdvertisementRate(float width, float height) {
        mHeight = (int) ((mWidth / (width + height)) * height);
        setLayoutParams();
    }

    /** * 开启轮播 */
    public void start() {
        handler.postDelayed(runnable, 2000);
    }

    /** * 关闭轮播 */
    public void stop() {
        handler.removeCallbacks(runnable);
    }

    public void setOnItemClickListener(OnItemClickListener _onItemClickListener) {
        this.mOnItemClickListener = _onItemClickListener;
    }

}

整体流程是这样的,初始化我们的View,为我们的Gallery绑定适配器,并监听点击事件与选中事件,这样方便回调,接着定义我们的定时器,这里传递数据源我写了一个方法:

    /** * 设置数据 如果是图片的URL的话,_isResourceId为false * * @param data * @param _isResourceId * true:本地图片资源ID false:网络图片URL */
    public void setData(ArrayList<AdvertisementObject> _data,
            boolean _isResourceId) {
        this.mAdvertisementObjectList = _data;
        this.isResourceId = _isResourceId;
        mMax = mAdvertisementObjectList == null ? 0 : mAdvertisementObjectList
                .size();
        // 设置指示器
        mIndicatorView.setTotal(mMax);
        setIndicatorParams();
        mImageSwitcherAdapter.notifyDataSetChanged();
    }

_isResourceId是用于区分是网络加载还是本地的资源ID,这样的原因是方便测试,因为通过网络加载图片不是本篇的重点,我在AdvertisementObject里放了两个参数,分别是资源ID,和通过网络加载的图片URL:

public class AdvertisementObject implements Serializable {
    public String mImageUrl;// 图片url
    public int mResourceId;//本地图片资源ID
}

当然你也可以加上更多的信息,完全取决于业务需求。

最后看看如何使用这个广告控件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <com.example.advertisementproject.view.AdvertisementView
        android:id="@+id/view_advertisement"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
    </com.example.advertisementproject.view.AdvertisementView>

</LinearLayout>
package com.example.advertisementproject;

import java.util.ArrayList;

import com.example.advertisementproject.view.AdvertisementView;
import com.example.advertisementproject.view.AdvertisementView.OnItemClickListener;
import com.example.advertisementproject.view.entity.AdvertisementObject;

import android.app.Activity;
import android.os.Bundle;
import android.widget.Toast;

public class MainActivity extends Activity {

    private AdvertisementView mAdvertisementView;
    private ArrayList<AdvertisementObject> advertisementObjects = new ArrayList<AdvertisementObject>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();
        initViews();
        initEvent();
    }

    private void initData() {
        AdvertisementObject advertisementObject = new AdvertisementObject();
        advertisementObject.mResourceId = R.drawable.image1;
        advertisementObjects.add(advertisementObject);
        advertisementObject = new AdvertisementObject();
        advertisementObject.mResourceId = R.drawable.image2;
        advertisementObjects.add(advertisementObject);
        advertisementObject = new AdvertisementObject();
        advertisementObject.mResourceId = R.drawable.image3;
        advertisementObjects.add(advertisementObject);
    }

    private void initViews() {
        mAdvertisementView = (AdvertisementView) findViewById(R.id.view_advertisement);
        mAdvertisementView.setAdvertisementRate(2, 1);
        mAdvertisementView.setData(advertisementObjects, true);
        mAdvertisementView.start();
    }

    private void initEvent() {
        mAdvertisementView.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(int position) {
                Toast.makeText(MainActivity.this, "position:" + position,
                        Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    protected void onPause() {
        mAdvertisementView.stop();
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        mAdvertisementView.stop();
        super.onDestroy();
    }

}

用法是不是很简单,基于上面的思路,大家能做出更炫的广告控件(最近写下一些自定义控件,主要是为了分享自定义控件的一个思路,希望大家喜欢。)

以下是完整的github项目地址
github项目源码地址:点击【项目源码】

你可能感兴趣的:(android,广告,控件,自定义view,轮播广告)