編輯:關於Android編程
感覺 Android 到處都是坑,每個地方都要把人折騰半天。
今天來簡單說說 Android之ActionBar、Tabs、Fragment、ViewPager 實現標簽頁切換並緩存頁面
關於他們的介紹就不多說了,網上到處都是,只說關鍵的部分:
我在開發的時候遇到幾個疑難問題,花費大量時間處理,總結如下:
1. 關於 Fragment 內部邏輯處理該寫在哪個事件回調部分?
2. ViewPager 頁面切換動畫卡頓,讓我頭疼了很久。
3. ViewPager 中如何保存 Fragment 當前視圖的狀態,讓 Tabs 頁面切換後不會重新加載,這地方很坑爹
4. ActionBar 中的 tab 很多時如何滾動顯示
解答:
一、Fragment 的事件回調:
package com.ai9475.meitian.ui.fragment; import android.app.Activity; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ListView; import com.ai9475.meitian.R; import com.ai9475.meitian.view.DiaryList; import com.ai9475.util.ZLog; /** * * Created by ZHOUZ on 14-1-21. */ public class DiaryListFragment extends BaseFragment { private static final String TAG = "DiaryListFragment"; @Override public void onAttach(Activity activity) { ZLog.i(TAG, "onAttach"); super.onAttach(activity); } @Override public void onCreate(Bundle savedInstanceState) { ZLog.i(TAG, "onCreate"); super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { ZLog.i(TAG, "onCreateView"); return inflater.inflate(R.layout.fragment_diary_list, container, false); } @Override public void onActivityCreated(Bundle savedInstanceState) { ZLog.i(TAG, "onActivityCreated"); super.onActivityCreated(savedInstanceState); ZLog.i(TAG, "DiaryList0"); DiaryList diaryList = new DiaryList( getActivity().getApplicationContext(), (ListView) getView().findViewById(R.id.diaryListCt) ); ZLog.i(TAG, "DiaryList load0"); diaryList.load("http://m.ai9475.com/?con=meitian_app"); this.setRetainInstance(true); } @Override public void onStart() { ZLog.i(TAG, "onStart"); super.onStart(); } @Override public void onResume() { ZLog.i(TAG, "onResume"); super.onResume(); } @Override public void onPause() { ZLog.i(TAG, "onPause"); super.onPause(); } @Override public void onStop() { ZLog.i(TAG, "onStop"); super.onStop(); } @Override public void onDestroyView() { ZLog.i(TAG, "onDestroyView"); super.onDestroyView(); } @Override public void onDestroy() { ZLog.i(TAG, "onDestroy"); super.onDestroy(); } @Override public void onDetach() { ZLog.i(TAG, "onDetach"); super.onDetach(); } }
主要復寫 onCreateView 方法,返回該 Fragment 所對應的視圖對象,這裡可以在返回視圖對象前進行一些簡單的配置,但千萬不要寫太耗時的處理阻塞UI主線程。
另外 onActivityCreated 方法,是當 activity 的 onCreate 事件結束時的回調,此時當前的Fragment對應的view已經並入到整個布局中,此時可以使用 getView() 方法獲取視圖對象。
其他幾個事件沒什麼太多可說,有些我也還不是太清楚,還有些動畫調用的事件。
二、切換頁面卡頓問題
這個問題的產生主要可能是兩方面,
1. 沒有使用 ViewPager 的緩存,每次切換都重新加載。
2. 加載 Fragment 內部有耗時耗資源的邏輯處理。
這裡主要說下第二種情況,我一開始沒處理掉 緩存問題時有一個解決辦法,
mViewPager = (ViewPager) findViewById(R.id.tabsViewPager); mViewPager.setOnPageChangeListener( new ViewPager.SimpleOnPageChangeListener() { private static final String TAG = "ViewPager.SimpleOnPageChangeListener"; private ArrayList hasLoadedPages = new ArrayList(); @Override public void onPageSelected(int position) { ZLog.i(TAG, "onPageSelected position:"+ position); getSupportActionBar().setSelectedNavigationItem(position); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { ZLog.i(TAG, "onPageScrolled position: "+ position +", positionOffset:"+ positionOffset +", positionOffsetPixels:"+ positionOffsetPixels); } @Override public void onPageScrollStateChanged(int state) { ZLog.i(TAG, "onPageScrollStateChanged"); int position = mViewPager.getCurrentItem(); switch (state) { // 正在拖動 case ViewPager.SCROLL_STATE_DRAGGING : ZLog.i(TAG, "ViewPager.SCROLL_STATE_DRAGGING position:"+ position); break; // 拖動釋放後正在沉降的過程 case ViewPager.SCROLL_STATE_SETTLING : ZLog.i(TAG, "ViewPager.SCROLL_STATE_SETTLING position:"+ position); break; // 切換動畫全部完成結束 case ViewPager.SCROLL_STATE_IDLE : ZLog.i(TAG, "ViewPager.SCROLL_STATE_IDLE position:"+ position); // 已加載過則不再加載 if (hasLoadedPages.contains(position)) break; Fragment fragment = mPager.getFragments().get(position); runCallback(position, fragment); hasLoadedPages.add(position); break; } } public void runCallback(int position, Fragment fragment) { ZLog.i(TAG, "runCallback"); DiaryList diaryList; switch (position) { case 0 : ZLog.i(TAG, "DiaryList0"); diaryList = new DiaryList( getApplicationContext(), (ListView) fragment.getView().findViewById(R.id.diaryListCt) ); ZLog.i(TAG, "DiaryList load0"); diaryList.load("http://m.ai9475.com/?con=meitian_app"); break; case 1: ZLog.i(TAG, "DiaryList1"); diaryList = new DiaryList( getApplicationContext(), (ListView) fragment.getView().findViewById(R.id.diaryListCt) ); ZLog.i(TAG, "DiaryList load1"); diaryList.load("http://m.ai9475.com/?con=meitian_app&act=hot"); break; case 2: ZLog.i(TAG, "DiaryList2"); diaryList = new DiaryList( getApplicationContext(), (ListView) fragment.getView().findViewById(R.id.diaryListCt) ); ZLog.i(TAG, "DiaryList load2"); diaryList.load("http://m.ai9475.com/?con=meitian_app"); break; } } }
當滾動動畫完全結束 case ViewPager.SCROLL_STATE_IDLE 時再執行 Fragment 的邏輯處理,這樣動畫就會流暢了。
但經過後來的測試發現有個很簡單的解決方案,就是下面要說到的 ViewPager 的緩存功能,其實很簡單。
三、緩存 Tabs 頁面切換不重新加載數據
我在這地方折騰的最久,而且很多時候無從下手的感覺,網上搜索了很多文章都有說道保存 Fragment 數據和狀態,但是沒有整整提到如何來保存他的當前view狀態,不知道如何保存,當然實際上我還是沒搞懂如何僅僅緩存 Fragment 的狀態,但對於 ViewPager 緩存 Tab 對應的 Fragment 還是找到了辦法,之前花了很大功夫來自己實現,後來偶然發現他居然有個自帶的方法
// 設置緩存多少個 Tab對應的 fragment mViewPager.setOffscreenPageLimit(6);
配置該項後,ViewPager在切換時將不會清理不可見的 Fragment,不會觸發 Fragment 的任何事件,因此也就不會導致其重新加載。
四、ActionBar 中的 Tabs
這個其實不用操作,在tabs數量超過一屏後,例如我現在設置6個寬度超過了屏幕則會變成可以橫向滾動的狀態,而不需要自己實現,之前不知道在這方面查了很多資料都無果,只知道可以用 tabhost 可以實現,但在ActionBar 裡面卻又用不了,自己試了下方多個tab才發現原來會自動實現,無語。
還是貼上 MainActivity.class 完整代碼:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+PC9wPgo8cHJlIGNsYXNzPQ=="brush:java;">package com.ai9475.meitian.ui;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.view.Menu;
import android.widget.ListView;
import com.ai9475.meitian.AppManager;
import com.ai9475.meitian.R;
import com.ai9475.meitian.ui.fragment.DiaryListFragment;
import com.ai9475.meitian.ui.fragment.Test2Fragment;
import com.ai9475.meitian.ui.fragment.Test3Fragment;
import com.ai9475.meitian.view.DiaryList;
import com.ai9475.util.ZLog;
import java.util.ArrayList;
public class MainActivity extends BaseActivity
{
private static final String TAG = "MainActivity";
private MyTabsPagerAdapter mPager;
private ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState)
{
ZLog.i(TAG, "start");
// 執行父級初始化方法
super.onCreate(savedInstanceState);
//requestWindowFeature(Window.FEATURE_NO_TITLE);
ZLog.i(TAG, "setContentView");
setContentView(R.layout.activity_main);
// 滑動頁面視圖配置
ZLog.i(TAG, "MyTabsPagerAdapter start");
mPager = new MyTabsPagerAdapter(getSupportFragmentManager());
mPager.getFragments().add(new DiaryListFragment());
mPager.getFragments().add(new Test2Fragment());
mPager.getFragments().add(new Test3Fragment());
mPager.getFragments().add(new Test3Fragment());
mPager.getFragments().add(new Test3Fragment());
mPager.getFragments().add(new Test3Fragment());
// 滑動分頁容器
mViewPager = (ViewPager) findViewById(R.id.tabsViewPager);
// 設置緩存多少個 fragment
mViewPager.setOffscreenPageLimit(6);
mViewPager.setAdapter(mPager);
// 頁面滑動事件
mViewPager.setOnPageChangeListener(
new ViewPager.SimpleOnPageChangeListener() {
private static final String TAG = "ViewPager.SimpleOnPageChangeListener";
private ArrayList hasLoadedPages = new ArrayList
主界面的實現 前面已做好了核心布局文件,接下來要做的就是讓客戶端活起來,現在的任務就是實現一個側滑菜單的功能,實現這個功能也並不難,使用V4包下的DrawerLayout
概要 對於mvp模式,大家都知道是由mvc演變而來的,對於MVC大家都知道 M Model(用於存放實體模型與業務邏輯) V View(存放布局和資源文件) C Co
Chromium的Extension由Page和Content Script組成。如果將Extension看作是一個App,那麼Page和Content Script就是
Spinner 列表選擇框Spinner是一個下拉列表,通常用於選擇一系列可選擇的列表項,它可以使用適配器,也可以直接設置數組源。android:entries=&rdq