android图片滚动选择器的实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012964944/article/details/70172885

之前的一篇文章《Android自定义view——滚动选择器》(没看过的同学建议先去了解一下)介绍了滚动选择器的原理,并实现了字符串选择器。现在要讲讲图片选择器的实现,以及通过选择器实现老虎机效果。

01

这里写图片描述


图片选择器

跟字符串选择器(StringScrollPicker)一样,图片选择器继承了ScrollPickerView,并在drawItem()方法里面实现图片的绘制。这里边提供了三种图片绘制模式:填充、居中、指定大小。

public class BitmapScrollPicker extends ScrollPickerView<Bitmap> {

    /**
     * 图片绘制模式:填充
     */
    public final static int DRAW_MODE_FULL = 1;
    /**
     * 图片绘制模式:居中
     */
    public final static int DRAW_MODE_CENTER = 2;
    /**
     * 图片绘制模式:指定大小
     */
    public final static int DRAW_MODE_SPECIFIED_SIZE = 3; 
     ...
    @Override
    public void drawItem(Canvas canvas, List<Bitmap> data, int position, int relative, float moveLength, float top) {
     ...
    }
    ...
}

老虎机

其实老虎机就是三个图片滚动选择器的组合,另外加上自动滚动及控制每个滚动器的结果。下面是老虎机SlotMachine的简单布局,主要是把三个滚动选择器的长度设置得比父容器稍高大一点,使得最上面和最下面的奖励只显示一半。
03

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="250dp"
             android:layout_gravity="center_horizontal"
             android:padding="24dp"
    >
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="288dp"
        android:layout_gravity="center_vertical"
        android:layout_margin="6dp"
        android:layout_weight="1"
        android:orientation="horizontal">
        <cn.forward.androids.views.BitmapScrollPicker
            android:id="@+id/slot_view_01"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            />
        <FrameLayout
            android:layout_width="2dp"
            android:layout_height="match_parent"
            android:layout_gravity="center_vertical"
            android:background="#bfbfbf"/>
        <cn.forward.androids.views.BitmapScrollPicker
            android:id="@+id/slot_view_02"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            />
        <FrameLayout
            android:layout_width="2dp"
            android:layout_height="match_parent"
            android:layout_gravity="center_vertical"
            android:background="#bfbfbf"/>
        <cn.forward.androids.views.BitmapScrollPicker
            android:id="@+id/slot_view_03"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            />
    </LinearLayout>
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/shape_slot_machine_border"
        />
</FrameLayout>

为了更接近老虎机的效果,当结果为没抽中时,使得其中两个选择器的奖励尽量相同,给玩家一种“差一点就中了”的感觉。同时,提供了回调接口给调用者来确定最终的结果,并采取相应的弥补措施。老虎机的关键代码如下:

/**
 * 老虎机
 */
public class SlotMachine extends FrameLayout implements ScrollPickerView.OnSelectedListener {

    // 滚动的时间
    private static int DURATION01 = 3000;
    private static int DURATION02 = 3500;
    private static int DURATION03 = 4000;

    private final static ArrayList<Integer> mDurationList = new ArrayList<Integer>(Arrays.asList(DURATION01, DURATION02, DURATION03));
...

    @Override
    public void onSelected(ScrollPickerView slotView, int position) {
        if (mIsPlaying) {
            mFinishedCounter++;
            if (slotView == mSlot01) {
                mSelectedArray[0] = position;
            } else if (slotView == mSlot02) {
                mSelectedArray[1] = position;
            } else if (slotView == mSlot03) {
                mSelectedArray[2] = position;
            }
            if (mFinishedCounter >= 3) {

                mFinishedCounter = 0;
                if (mSlotMachineListener != null) {
                    boolean win = false;
                    boolean makeup = false;
                    if (mSelectedArray[0] == mSelectedArray[1] && mSelectedArray[0] == mSelectedArray[2]) { // win
                        // 是否取消中奖,采用弥补动画,使之变成不中奖的结果
                        win = mSlotMachineListener.acceptWinResult(mSelectedArray[0]);
                        makeup = !win;
                    }
                    final boolean finalMakeup = makeup;
                    Runnable task = new Runnable() {
                        public void run() {
                            if (finalMakeup) {
                                mSelectedArray[2] = mSlot03.getSelectedPosition();
                            }
                            mSlotMachineListener.onFinish(mSelectedArray[0], mSelectedArray[1], mSelectedArray[2]);
                            mIsPlaying = false;
                        }
                    };

                    if (makeup) {
                        makeUpPurchaseFailed(mSelectedArray[2]);
                        // 等待弥补动画结束
                        ThreadUtil.getInstance().runOnMainThread(task, 1200);
                    } else {
                        task.run();
                    }
                }
            }
        }
    }


    /**
     * 开始滚动,
     *
     * @param prizePosition 奖品的索引,如果prizePosition<0或者 prizePosition>=总的奖品数,则表示不中奖
     */
    public boolean play(int prizePosition) {
        if (!isClickable() || mIsPlaying) {
            return false;
        }
        mFinishedCounter = 0;
        mIsPlaying = true;

        int slot01, slot02, slot03;
        int duration01, duration02, duration03;

//        Collections.shuffle(mDurationList);
        duration01 = mDurationList.get(0);
        duration02 = mDurationList.get(1);
        duration03 = mDurationList.get(2);

        if (prizePosition < 0 || prizePosition >= mPrizeList.size()) { // 不中奖,控制三个中有两个相同
            // pos01表示时间最短的停留位置,pos03表示时间最长
            int pos01, pos02, pos03;
            pos01 = mRandom.nextInt(mPrizeList.size());
            if (mRandom.nextInt(3) == 0) { // 01,02相同的概率为1/3
                pos02 = pos01;
                pos03 = mRandom.nextInt(mPrizeList.size());
            } else {
                pos02 = mRandom.nextInt(mPrizeList.size());
                if (mRandom.nextInt(4) == 0) { // 01,03相同的概率为1/4
                    pos03 = pos01;
                } else {
                    pos03 = mRandom.nextInt(mPrizeList.size());
                }
            }
            if (pos01 == pos02 && pos01 == pos03) {
                pos01 = (pos01 + 1) % mPrizeList.size();
            }

            // 按照时间排序老虎机的窗口,如[1,3,2]表示slot01的时间最短,接着是slot03,slot02
            HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
            map.put(mDurationList.indexOf(DURATION01) + 1, pos01); // 时间最短的那个slot停留的位置
            map.put(mDurationList.indexOf(DURATION02) + 1, pos02);
            map.put(mDurationList.indexOf(DURATION03) + 1, pos03);

            slot01 = map.get(1);
            slot02 = map.get(2);
            slot03 = map.get(3);
        } else {
            slot01 = slot02 = slot03 = prizePosition;
        }

        mSlot01.autoScrollFast(slot01, duration01);
        mSlot02.autoScrollFast(slot02, duration02);
        mSlot03.autoScrollFast(slot03, duration03);
        return true;
    }

    /**
     * 弥补购买失败,使其中一个往下滚动一列,造成未抽中的假象
     *
     * @param prizePosition
     */
    public void makeUpPurchaseFailed(int prizePosition) {
        int moveY = mSlot03.getItemHeight();
        mSlot03.autoScrollTo(moveY, 1200, new LinearInterpolator(), false);
    }

    public interface SlotMachineListener {
        /**
         * 滚动结束时回调
         * @param pos01
         * @param pos02
         * @param pos03
         */
        void onFinish(int pos01, int pos02, int pos03);

        /**
         * 是否接受该次中奖结果
         * @param position
         * @return 返回true则表示确认该次赢得奖品,false则表示取消该次奖品
         */
        boolean acceptWinResult(int position);
    }
}

完整的代码放在了github上:https://github.com/1993hzw/Androids,谢谢大伙的支持!

猜你喜欢

转载自blog.csdn.net/u012964944/article/details/70172885