Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 自定義View 之利用ViewPager 實現畫廊效果(滑動放大縮小)

自定義View 之利用ViewPager 實現畫廊效果(滑動放大縮小)

編輯:關於Android編程

基本介紹

畫廊在很多的App設計中都有,如下圖所示:
這裡寫圖片描述

該例子是我沒事的時候寫的一個小項目,具體源碼地址請訪問https://github.com/AlexSmille/YingMi。

該畫廊類似封面的效果,滑到中間的圖片會慢慢變大,離開的View會慢慢的縮小,同時可設置滑動監聽和點擊監聽。

網上有很多例子都是通過Gallery實現的,而上例的實現是通過ViewPager實現,解決了性能優化的問題,今天特此把它抽出來,封裝一下,以便以後的方便使用。最終實現的效果如下:

這裡寫圖片描述

使用方式

布局中添加該自定義控件



    
    



代碼中設置

代碼中設置分為以下幾個步驟:

查找控件 初始化數據 將需要顯示的數據設置到控件上 設置滑動監聽
public class MainActivity extends AppCompatActivity {

    private CoverFlowViewPager mCover;

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

        mCover = (CoverFlowViewPager) findViewById(R.id.cover);

        // 初始化數據
        List list = new ArrayList<>();
        for(int i = 0;i<10;i++){
            ImageView img = new ImageView(this);
            img.setBackgroundColor(Color.parseColor("#"+getRandColorCode()));
            list.add(img);
        }
        //設置顯示的數據
        mCover.setViewList(list);
        // 設置滑動的監聽,該監聽為當前頁面滑動到中央時的索引
        mCover.setOnPageSelectListener(new OnPageSelectListener() {
            @Override
            public void select(int position) {
                Toast.makeText(getApplicationContext(),position+"",Toast.LENGTH_SHORT).show();
            }
        });
    }


    /**
     * 獲取隨機顏色,便於區分
     * @return
     */
    public static String getRandColorCode(){
        String r,g,b;
        Random random = new Random();
        r = Integer.toHexString(random.nextInt(256)).toUpperCase();
        g = Integer.toHexString(random.nextInt(256)).toUpperCase();
        b = Integer.toHexString(random.nextInt(256)).toUpperCase();

        r = r.length()==1 ? "0" + r : r ;
        g = g.length()==1 ? "0" + g : g ;
        b = b.length()==1 ? "0" + b : b ;

        return r+g+b;
    }
}

實現原理

實現過程中有兩個難點:

如何實現滑動過程中的放大與縮小 如何顯示ViewPager中未被顯示的頁面

如何實現滑動過程中的放大與縮小

在設置每一個ViewPager 的頁面時,對每一個頁面都設置一個固定的padding值,這樣每個頁面都會顯示縮小狀態。同時ViewPager設置addOnPageChangeListener(),滑動監聽,在該滑動監聽中會回調ViewPager的滑動的狀態,滑動的偏移量等,根據滑動的偏移量進行放大縮小。及根據padding值設置控件的顯示大小

如何顯示ViewPager中未被顯示的頁面

xml中有一個不常用的屬性android:clipChildren,是否限制子View的顯示。設置為false,則子View的顯示不受父控件的限制。

代碼實現

編寫控件的布局文件



    


一個相對布局中嵌入一個ViewPager,相對布局用於確定顯示的范圍,ViewPager用以實現滑動,放大縮小等。

創建CoverFlowViewPager,加載布局

/**
 *
 *  實現封面浏覽
 * Created by alex_mahao on 2016/8/25.
 */
public class CoverFlowViewPager extends RelativeLayout implements OnPageSelectListener {
    /**
     * 用於左右滾動
     */
    private ViewPager mViewPager;

    public CoverFlowViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        inflate(context, R.layout.widget_cover_flow,this);
        mViewPager = (ViewPager) findViewById(R.id.vp_conver_flow);
        //init();
    }

查找控件,並加載布局。

編寫適配器,實現滑動的監聽

既然有了ViewPager,那麼肯定要有適配器Adapter。因為我們要在滑動監聽中,根據偏移量操作每一個子元素,放大或縮小,而對於子元素,當然適配器最容易獲取,所以將Adapter實現了ViewPager的滑動監聽接口。

/**
 * 滾動的適配器
 * Created by alex_mahao on 2016/8/25.
 */
public class CoverFlowAdapter extends PagerAdapter implements ViewPager.OnPageChangeListener {

    /**
     *  默認縮小的padding值
     */
    public static int sWidthPadding;

    public static int sHeightPadding;

    /**
     * 子元素的集合
     */
    private List mViewList;

    /**
     * 滑動監聽的回調接口
     */
    private OnPageSelectListener listener;

    /**
     * 上下文對象
     */
    private Context mContext;

    public CoverFlowAdapter(List mImageViewList, Context context) {
        this.mViewList = mImageViewList;
        mContext = context;
        // 設置padding值
        sWidthPadding = dp2px(24);
        sHeightPadding = dp2px(32);
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView(mViewList.get(position));
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        View view = mViewList.get(position);
        container.addView(view);

        return view;
    }

    @Override
    public int getCount() {
        return mViewList == null ? 0 : mViewList.size();
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        // 該方法回調ViewPager 的滑動偏移量
        if (mViewList.size() > 0 && position < mViewList.size()) {
            //當前手指觸摸滑動的頁面,從0頁滑動到1頁 offset越來越大,padding越來越大
            Log.i("info", "重新設置padding");
            int outHeightPadding = (int) (positionOffset * sHeightPadding);
            int outWidthPadding = (int) (positionOffset * sWidthPadding);
            // 從0滑動到一時,此時position = 0,其應該是縮小的,符合
            mViewList.get(position).setPadding(outWidthPadding, outHeightPadding, outWidthPadding, outHeightPadding);

            // position+1 為即將顯示的頁面,越來越大
            if (position < mViewList.size() - 1) {
                int inWidthPadding = (int) ((1 - positionOffset) * sWidthPadding);
                int inHeightPadding = (int) ((1 - positionOffset) * sHeightPadding);
                mViewList.get(position + 1).setPadding(inWidthPadding, inHeightPadding, inWidthPadding, inHeightPadding);
            }
        }

    }

    @Override
    public void onPageSelected(int position) {
        // 回調選擇的接口
        if (listener != null) {
            listener.select(position);
        }
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }

    /**
     * 當將某一個作為最中央時的回調
     *
     * @param listener
     */
    public void setOnPageSelectListener(OnPageSelectListener listener) {
        this.listener = listener;
    }


    /**
     * dp 轉 px
     *
     * @param dp
     * @return
     */
    public int dp2px(int dp) {
        int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, mContext.getResources().getDisplayMetrics());

        return px;
    }

}

改代碼分為兩部分,PagerAdapter實現,滑動監聽的實現。PagerAdapter的實現不在多說,最基礎的東西。重點在滑動監聽的實現。

滑動監聽有三個回調方法:其中onPageScrolled(int position, float positionOffset, int positionOffsetPixels)回調的便是ViewPager的滑動得偏移量,我們再次動態的設置相應元素的padding值,實現放大縮小。

onPageSelected()為選中的回調,通過自定義接口的方式回調給其調用者。後面會提。

初始化ViewPager

既然有了適配器,那麼自然就開始編寫適配器的部分:

  /**
     * 初始化方法
     */
    private void init() {
        // 構造適配器,傳入數據源
        mAdapter = new CoverFlowAdapter(mViewList,getContext());
        // 設置選中的回調
        mAdapter.setOnPageSelectListener(this);
        // 設置適配器
        mViewPager.setAdapter(mAdapter);
        // 設置滑動的監聽,因為adpter實現了滑動回調的接口,所以這裡直接設置adpter
        mViewPager.addOnPageChangeListener(mAdapter);
        // 自己百度
        mViewPager.setOffscreenPageLimit(5);

        // 設置觸摸事件的分發
        setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // 傳遞給ViewPager 進行滑動處理
                return mViewPager.dispatchTouchEvent(event);
            }
        });
    }

注釋很詳細,唯一需要解釋的便是事件的分發。

我們的ViewPager的大小是固定的,只有中間的顯示區域,那麼對於手指在兩個側邊滑動時,ViewPager自然接受不到觸摸事件,通過設置外層相對布局的觸摸事件監聽,將觸摸的事件傳遞到ViewPager,實現滑動ViewPager之外區域時,ViewPager仍能夠實現對應的滑動。

數據源的包裝

適配器有了,ViewPager也有了,那麼只剩下數據源了。

因為我們是根據設置padding值實現的,那麼對於需要顯示的控件,他的背景將無法實現放大縮小,所以對控件在包裝一層外部控件,這樣設置外部控件的padding值,自然需要顯示的控件會放大縮小。

  /**
     * 設置顯示的數據,進行一層封裝
     * @param lists
     */
    public void setViewList(List lists){
        if(lists==null){
            return;
        }
        mViewList.clear();
        for(View view:lists){

            FrameLayout layout = new FrameLayout(getContext());
            // 設置padding 值,默認縮小
            layout.setPadding(CoverFlowAdapter.sWidthPadding,CoverFlowAdapter.sHeightPadding,CoverFlowAdapter.sWidthPadding,CoverFlowAdapter.sHeightPadding);
            layout.addView(view);
            mViewList.add(layout);
        }
        // 刷新數據
        mAdapter.notifyDataSetChanged();
    }

選中監聽的回調

當我們滑動時,可能會根據不同的滑動,顯示不同的數據。

通過設置滑動監聽之後,對onPageSelected實現層層的接口回調。

接口的定義OnPageSelectListener

public interface OnPageSelectListener {

    void select(int position);
}

CoverFlowAdapter中添加回調

    @Override
    public void onPageSelected(int position) {
        // 回調選擇的接口
        if (listener != null) {
            listener.select(position);
        }
    }

CoverFlowViewPager中添加回調

 // 顯示的回調
    @Override
    public void select(int position) {
        if(listener!=null){
            listener.select(position);
        }
    }

點擊事件的設置

直接對數據源循環設置監聽即可

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved