1. 程式人生 > >android圖片滾動選擇器的實現

android圖片滾動選擇器的實現

之前的一篇文章《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);
    }
}