1. 程式人生 > >不用ViewPager和Fragment實現滑動頁面的效果

不用ViewPager和Fragment實現滑動頁面的效果

這是一篇被逼出來的文章。
一入SDK深似海,從此jar包是路人,沒錯,你以為我願意不用ViewPager和Fragment啊,因為SDK為了減少包體大小不能用v4的包啊!坑爹的v4包居然有1M多,你們可真能寫啊。我相信一定有朋友會建議說,把v4包裡相關的類摳出來用啊,呵呵噠,祝你摳的愉快。

言歸正傳,ViewPager和Fragment那是一套相當龐大的介面框架,想要自己實現一個功能相似且能完美的控制記憶體和介面生命週期,短期單人幾乎是不可能完成的任務,我們只能退而求其次,把底層複雜的邏輯都剝離,再保證沒有記憶體洩漏的情況下,實現介面上看起來相似的功能。大概分析一下滑動介面的需求,抽象出來看就是有N個寬度和螢幕寬度(或者window寬度)一樣的介面排排坐,當用戶滑動的時候不是緩緩過度到下一個頁面,而是有一個彈性效果直接到達下一個頁面,每一個頁面的容器就是Fragment,而N個頁面的容器,就是ViewPager,ViewPager的容器就是我們的Activity。
理解了這個,我們就可以考慮用其他容器來代替ViewPager和Fragment了,橫向滑動的第一選擇當然是HorizontalScrollView,而Fragment和PageAdapter只能我們自己來實現了,本質就是個View。
直接上程式碼,先自定義一個HorizontalScrollView來實現ViewPager的功能:

/**
 * 
 * @author Amuro
 * 
 */
public class ScrollViewPager extends HorizontalScrollView
{
    public interface OnPageChangedListener
    {
        void onChange(int index);
    }

    private OnPageChangedListener listener;

    public void setOnPageChangedListener(OnPageChangedListener listener)
    {
        this
.listener = listener; } private void notifyPageChanged() { if (lastPage != currentPage) { lastPage = currentPage; if (listener != null) { listener.onChange(currentPage); } } } private int
subChildCount = 0; private int downX = 0; private int lastPage = 0; private int currentPage = 0; private ArrayList<Integer> pointList = new ArrayList<Integer>(); public ScrollViewPager(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } public ScrollViewPager(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ScrollViewPager(Context context) { super(context); init(); } private GestureDetector mGestureDetector; private void init() { setHorizontalScrollBarEnabled(false); mGestureDetector = new GestureDetector(getContext(), new HScrollDetector()); } // Return false if we're scrolling in the y direction class HScrollDetector extends SimpleOnGestureListener { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { if (Math.abs(distanceX) > Math.abs(distanceY)) { return true; } return false; } } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: downX = (int) ev.getX(); break; } return super.onInterceptTouchEvent(ev) && mGestureDetector.onTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { // case MotionEvent.ACTION_DOWN: // downX = (int) ev.getX(); // break; case MotionEvent.ACTION_MOVE: break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { if (Math.abs((ev.getX() - downX)) > getWidth() / 4) { if (ev.getX() - downX > 0) { smoothScrollToPrePage(); } else { smoothScrollToNextPage(); } notifyPageChanged(); } else { smoothScrollToCurrent(); } return true; } } return super.onTouchEvent(ev); } private void smoothScrollToCurrent() { smoothScrollTo(pointList.get(currentPage), 0); } private void smoothScrollToNextPage() { if (currentPage < subChildCount - 1) { currentPage++; smoothScrollTo(pointList.get(currentPage), 0); } } private void smoothScrollToPrePage() { if (currentPage > 0) { currentPage--; smoothScrollTo(pointList.get(currentPage), 0); } } public boolean gotoPage(int page) { if (page > 0 && page < subChildCount - 1) { smoothScrollTo(pointList.get(page), 0); currentPage = page; notifyPageChanged(); return true; } return false; } public void setAdapter(PagerAdapter<?> adapter) { LinearLayout container = (LinearLayout) this.getChildAt(0); adapter.setContainer(container); adapter.notifyDatasetChanged(); // receiveChildInfo(); subChildCount = adapter.getCount(); for (int i = 0; i < subChildCount; i++) { pointList.add(0 + Constants.HOME_VIEW_WIDTH * i); } } }

核心程式碼在onTouchEvent方法裡,熟悉安卓觸控事件並瞭解下拉重新整理原理的童鞋應該一看就懂的程式碼,就不贅述了,無非就是判斷使用者手勢在橫向上的滑動距離,當超過一定距離就認為使用者是主動滑動到下一頁,通過scoller幫助使用者來實現這個滑動的彈性效果。
這裡我們用到了GestureDetector這個類,目的是為了解決滑動衝突的問題,後面我會再單獨安排文章講這個事兒,這裡先不表。

有了“ViewPager”,下面就是Fragment了,我們先定義一個介面:

public interface IViewController
{
    View getView();
    void create();
    void onShow();
}

其實Fragment的本質就是一個View控制器,為了簡單,這裡就寫幾個主要的回調了。然後Fragment和ViewPager直接的黏合劑PageAdapter我們也仿照著寫一個

public abstract class PagerAdapter<T>
{
    protected List<T> pages;
    protected ViewGroup container;

    public PagerAdapter(List<T> pages)
    {
        this.pages = pages;
    }

    protected void setContainer(ViewGroup container)
    {
        this.container = container;
    }

    public abstract int getCount();
    public abstract void notifyDatasetChanged();
    protected abstract T instantiateItem(int positioin);
}

其中container就是我們設定的父容器,一般情況下都是ViewPager,第二個方法大家一看就知道不用多說,第三個方法是給子類初始化具體的fragment來用的,好,針對我們的ViewController,我們來擴充套件這個類:

package cn.cmgame2_0.launch_model.shortcut.main;

import java.util.List;

import cn.cmgame2_0.launch_model.shortcut.main.V.base.IViewController;
import cn.cmgame2_0.utils.custom_view.PagerAdapter;

public class HomeViewPageAdapter extends PagerAdapter<IViewController>
{
    public HomeViewPageAdapter(List<IViewController> pages)
    {
        super(pages);
    }

    @Override
    public int getCount()
    {
        return pages.size();
    }

    @Override
    protected IViewController instantiateItem(int positioin)
    {
        IViewController vc = pages.get(positioin);
        container.addView(vc.getView());

        return vc;
    }

    @Override
    public void notifyDatasetChanged()
    {
        container.removeAllViews();
        for(int i = 0; i < getCount(); i++)
        {
            instantiateItem(i);
        }
    }

}

這裡再回去看一下上面自定義HorizontalScrollView的setAdapter方法,其實就把ViewPager中的根佈局作為container傳給Adapter,然後adapter中會把設定好的ViewController所有view新增到container中。
好,容器和介面卡都有了,下面我們根據我們的介面需求去新增具體的ViewController就行了,貼一個例子:


public class RecommendViewController extends ShortcutViewController implements RecommendV
{
    private RecommendPresenter presenter;
    private long lastRefreshTime = 0;

    public RecommendViewController(Context context)
    {
        super(context);
    }

    private GridView gridViewInstalled;
    private InstalledAdapter installedAdapter;
    private GridView gridViewRecommend;
    private RecommendAdapter recommendAdapter;
    private Button buttonRefresh;

    private ProgressDialog progressDialog;

    @Override
    public void create()
    {
        presenter = new RecommendPresenter(this);
        rootView = new RecommendView(context);
        initView();
    }

    private void initView()
    {
        gridViewInstalled = (GridView)findViewById(RecommendView.id_gv_installed);
        gridViewRecommend = (GridView)findViewById(RecommendView.id_gv_recommend);
        buttonRefresh = (Button)findViewById(RecommendView.id_bt_refresh);
        progressDialog = DialogUtils.getProgressDialog(context);

        installedAdapter = new InstalledAdapter(context, presenter.getInstalledGames());
        gridViewInstalled.setAdapter(installedAdapter);
        gridViewInstalled.setOnItemClickListener(new OnItemClickListener()
        {

            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id)
            {

            }
        });

        recommendAdapter = new RecommendAdapter(context, presenter.getRecommendGames());
        gridViewRecommend.setAdapter(recommendAdapter);

        buttonRefresh.setOnClickListener(new OnClickListener()
        {

            @Override
            public void onClick(View v)
            {

            }
        });
    }

    @Override
    public void onShow()
    {
    }

    @Override
    public void showLoading()
    {
        progressDialog.show();
    }

    @Override
    public void hideLoading()
    {
        progressDialog.dismiss();
    }

    @Override
    public void onError(String errorCode, String errorMsg)
    {
        ToastUtils.showToast(context, "");
    }

    @Override
    public void onDataFetched(List<RecommendBean> data)
    {
        recommendAdapter.notifyDataSetChanged();
    }

    @Override
    public Context getContext()
    {
        return context;
    }
}

刪除了所有涉及公司業務的程式碼,不過大概框架各位也能看懂了,因為不能用xml來構建介面,這裡還需要構建一套替代的框架,其實就是把所有View構建的程式碼拉出去獨立成一個體系就好啦,很簡單,不再贅述。眼尖的童鞋應該還看到了程式碼裡的V和Presenter,沒錯,這裡還嘗試使用了最新的MVP架構來解耦介面控制與資料操作,後面再開新文章講。

最後在Activity裡把這些元素組織到一起就大功告成了:

private void initViewPager()
    {
        if(bean.collectionList == null || bean.collectionList.size() == 0)
        {
            pageCount = 1;
        }
        else
        {
            pageCount = bean.collectionList.size() + 1;
        }

        final List<IViewController> vcList = new ArrayList<IViewController>();

        for(int i = 0; i < pageCount; i++)
        {
            IViewController vc = null;
            if(i == 0)
            {
                vc = new RecommendViewController(this);
                vc.create();
            }
            else
            {
                vc = new CollectionViewController(this, i - 1);
                vc.create();
            }
            vcList.add(vc);
        }

        HomeViewPageAdapter adapter = new HomeViewPageAdapter(vcList);
        viewPager.setAdapter(adapter);

        viewPager.setOnPageChangedListener(new OnPageChangedListener()
        {

            @Override
            public void onChange(int index)
            {
                indicatorManager.change(index);
                vcList.get(index).onShow();
            }
        });

    }

好的api就是要讓使用者用起來和他最熟悉的一模一樣,這也是每個寫框架的童鞋要給自己最起碼的要求。最後貼兩張效果圖:
這裡寫圖片描述
這裡寫圖片描述

就醬~

相關推薦

不用ViewPagerFragment實現滑動頁面效果

這是一篇被逼出來的文章。 一入SDK深似海,從此jar包是路人,沒錯,你以為我願意不用ViewPager和Fragment啊,因為SDK為了減少包體大小不能用v4的包啊!坑爹的v4包居然有1M多,你們可真能寫啊。我相信一定有朋友會建議說,把v4包裡相關的類摳出

Android ViewPagerFragment實現仿微信導航介面及滑動效果

1 先看看實現的效果: ps:上面每一幀Fragment中,包含是來自網路的圖片; 實現ViewPager+Fragment的頁面滑動和底部導航原理 主佈局檔案如下: <?xml version="1.0" encoding="utf-8"?> <L

Android ViewPagerFragment實現頂部導航介面滑動效果、標籤下的tab位置

在專案中,我們常常需要實現介面滑動切換的效果。例如,微信介面的左右滑動切換效果。那這種效果是怎麼實現的?今天我就帶大家簡單瞭解ViewPager,並通過例項來實現該效果。 一. ViewP

ViewPager+Fragment 實現滑動頁面效果

佈局檔案如下: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

viewpager + fragment實現滑動切換效果

先上一張效果圖(非動圖,但是可以實現滑動切換,點選下方按鈕,也可以進行切換): MainActivity之中的程式碼 public class Main2Activity extends AppCompatActivity implements View.OnClickListener{

使用TabLayout、ViewPagerFragment實現頂部選單可滑動切換

效果圖如下 首先,要使用控制元件需要新增design library,在Android Studio中新增 compile 'com.android.support:design:23.4.0' 然後是佈局檔案 <?xml version="1.0" encod

安卓介面之ViewpagerTablayout實現滑動介面

摘要:六部實現選項卡介面   一. 在gradle檔案新增以下程式碼: implementation 'com.android.support:design:28.0.0'  在gradle檔案新增以上程式碼後,才能使用Tablayout(版本號28.0.0是我做實驗時

安卓界面之ViewpagerTablayout實現滑動界面

多個 ces listt man bind tle find you 安卓 摘要:六部實現選項卡界面 一. 在gradle文件添加以下代碼: implementation ‘com.android.support:design:28.0.0‘ 在gradle文件添

利用ViewPagerFragment實現頁卡切換

  在微信和QQ中,頁面可以通過滑動切換,在Android應用開發中,利用ViewPager和Fragment可以實現這一功能。  開發工具:Android Studio 3.0  程式碼示例:  activity_a.xml(佈局):<android.support.

ViewPagerFragment結合,利用(HorizontalScrollView)實現指示器與ViewPager同時滑動的動態效果

首先是佈局檔案,佈局中需要一個HorizontalScrollView用來承載所有標題和指示器,注意HorizontalScrollView中只能有一個子佈局,所以要新增一個Linearlayout(豎向的),這裡為什麼是豎向的呢?因為這個線性佈局中要加

TabLayout+ViewPager+Fragment實現滑動效果

實現的效果圖如下: 一、頁面佈局檔案  1. 主頁面tab_main.xml,程式碼如下: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://sc

Android Studio 使用ViewPager + Fragment實現滑動選單Tab效果 --簡易版

描述:         之前有做過一個記賬本APP,拿來練手的,做的很簡單,是用Eclipse開發的;         最近想把這個APP重新完善一下,添加了一些新的功能,並選用Android Studio來開發;         APP已經完善了一部分,現在就想把已經做好的功能整理一下,記錄下來。 效果圖

Viewpager+Fragment實現滑動,點選滑動效果

佈局頁面 佈局有很多種方式,為了美觀,這裡我們就用RadioGroup實現 <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_widt

ViewPager實現滑動Fragment滑動頁面時報錯的解決方案

1. 主Activity和主佈局,線性佈局中只有一個ViewPager控制元件: MainActivity.java activity.xml 2. 三個繼承Fragment的類和對應佈局: FragmentOne.java FragmentTwo.java Fragmen

ViewPager+RadioGroup+RadioButton實現滑動切換頁面與點選按鈕切換頁面

一:效果圖: 二:程式碼: 首先  根據我們有幾個頁面就設定幾個Fragment, 主函式: public class MainActivity extends AppCompatActivity { private ViewPager viewpager;

【FastDev4Android框架開發】HorizontalScrollView,Fragment,FragmentStatePagerAdapter打造網易新聞Tab及滑動頁面效果(三十六)

轉載請標明出處:(一).前言:            【好訊息】個人網站已經上線執行,後面部落格以及技術乾貨等精彩文章會同步更新,請大家關注收藏:http://www.lcode.org         仿36Kr客戶端開發過程中,因為他們網站上面的新聞文章分類比較多,所以我

TabLayout、ViewPagerfragment實現滑動的頂部選單

首先看下效果 第一步:主佈局 <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_co

Android開發ViewPagerFragment結合使用實現新聞類app( 三 )(基本成型的app)

//該類為我們的標題欄的自定義View public class MyLinearLayout extends LinearLayout { public MyLinearLayout(Context context, AttributeSet attrs) { super(cont

結合Tab,ViewPagerFragment實現簡單分頁滑動

在APP設計當中,使用ViewPager和Fragment來實現分頁滑動並不少見,該設計可以利用少量的空間來實現多內容的展示。效果圖如下: 以下是實現該功能的程式碼: MainActivity public class MainActivity e

安卓開發:viewpager + fragment 實現滑動切換

時間緊迫 長話短說 以後再補上 佈局檔案、 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.co