編輯:關於Android編程
演示ViewPager的預加載功能
Android ViewPager 的預加載
android新引入的Fragment加入了Viewpager這樣的一個組件。至於該控件的功能在這裡就不說了。這篇文章主要解決fragmentviewpager預加載的問題。默認的viewpager是android 新引入的Fragment加入了Viewpager這樣的一個組件。至於該控件的功能在這裡就不說了。這篇文章主要解決fragment viewpager預加載的問題。默認的viewpager是會預先加載下一個fragment的控件的,可以通過setOffscreenPageLimit(int limit) 來設置要提前加載的fragment。即是說當limit等於5的時候,打開第一個fragment的時候就相當於同時打開了其他的4個fragment了。這樣切換的時候將會非常流暢。但是,當每個fragment都需要去聯網加載網絡數據或者做一些耗時的操作,而且有其他的fragment並不是必須的,用戶不一定會打開。這樣的話如果還預加載的話將會浪費資源,用戶體驗也不好。雖然可以通過setOffscreenPageLimit(0)來設置不提前預加載,但這樣的設置沒有效果。通過研究ViewPager的源碼,可以設置該類默認的DEFAULT_OFFSCREEN_PAGES = 0,來預防預加載。
*ViewPager默認加載第0頁,而真正預加載第1頁
之前的Guide項目繼續演示Viewpager的預加載功能:
GuideActivity.java //給viewpager添加一個view的方法 @Override public Object instantiateItem(ViewGroup container, int position) { //container就是viewpager對象 Log.i(tag, "instantiateItem position = "+position); container.addView(imageList.get(position)); return imageList.get(position); } //給viewpager移除view的方法 @Override public void destroyItem(ViewGroup container, int position, Object object) { Log.i(tag, "destroyItem position = "+position); container.removeView((View)object); }
重點!!(安卓中控件沒有寬高-->寬高設置)用案例說明之前的用java代碼給布局做手機適配
之前案例:用java代碼給布局做手機適配,問題是,為什麼要先拿到RelativeLayout的寬高後再設置到TextView控件上呢?為什麼?
public class MainActivity extends Activity { private static final String tag = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); TextView textView = (TextView) findViewById(R.id.tv); //獲取屏幕寬高 DisplayMetrics outMetrics = new DisplayMetrics(); //必須調用此api才可獲取寬高值 getWindowManager().getDefaultDisplay().getMetrics(outMetrics); Log.i(tag,"outMetrics.heightPixels = "+outMetrics.heightPixels); Log.i(tag,"outMetrics.widthPixels = "+outMetrics.widthPixels); //先將當前的寬高設置給textview所在父布局中,作用在textView上。 RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams( //數學四捨五入 //320px (int)(160px+0.5) 160px //321px 160.5px 接近161px (int)(160.5px+0.5px) 161px //321.2px 160.6px 161px (int)(161.1px) 161px (int)(outMetrics.widthPixels*0.5+0.5), (int)(outMetrics.heightPixels*0.5+0.5)); textView.setLayoutParams(layoutParams); } }
演示項目Button_layout來解釋上面的問題
自定義一個button_item.xml,在裡面的button控件設置的是寬高都300dp
在MainActivity裡用代碼去讓activitymain.xml的對象去使用addView去把buttonitem.xml的對象加到activity_main.xml布局中,把其跑到模擬器中發現其button控件設置的是寬高300dp都失效了。
activity_main.xmlMainActivity.java public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RelativeLayout layout = (RelativeLayout) findViewById(R.id.rl); View view = View.inflate(getApplicationContext(), R.layout.button_item, null); layout.addView(view); } }
原因:那是因為控件本身(除非自己是父布局)是沒有寬高的,是通過父布局把寬高給控件。
安卓sdk-->tools--->Hierarchy Viewer工具裡有顯示說明
還有把之前的adapter_java裡的RelativeLayout.LayoutParams改成LinearLayout.LayoutParams這樣不可以,因為控件的父布局不匹配。
演示Json
工程zhbj+左滑側拉欄的開源項目+XUtils
讓zhbj(智慧北京)項目去關聯這兩個library
該zhbj項目需要配合tomcat服務器
項目是UTF-8的編碼,我們變成需要注意,以後也一致使用UTF-8
新建一個Demo,把Tomcat服務端裡的zhbj資源(Json資源)放入到Demo裡的assert目錄下,ctrl+H去搜索資源裡的原來的IP後修改成現在的IP,修改完畢後復制回到Tomcat
用浏覽器去浏覽服務器中的json文件:
思考客戶端zhbj怎麼和服務端去做交互?
讓zhbj(智慧北京)工程訪問該服務器的json,訪問前注意修改訪問路徑
老師資料裡有Json的格式化工具HiJson
講解json和zhbj之間的數據交互細節 .
公司的接口文檔:是用來告訴員工如何設計json和服務器做交互
分析智慧北京主頁面的布局及使用到的技術,並使用技術2來演示實現智慧北京的主頁面
繼續在昨天的引導Demo項目:Guide(我們將使用Fragment+RadioGroup技術設計該主頁)
MainActivity實現智慧北京主頁面,把frag_home.xml設置其背景布局資源
MainActivity.java public class MainActivity extends FragmentActivity { private FrameLayout layout_content; private RadioGroup main_radio; private int index; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.frag_home); frag_home.xml<framelayout android:id="@+id/layout_content" android:layout_height="0dip" android:layout_weight="1.0" android:layout_width="match_parent"> strings.xml</framelayout> 引導界面 Settings Hello world! 首頁 新聞中心 智慧服務 政務指南 設置
frag_home.xml使用RadioGroup
1、在MainActivity獲取到布局中的RadioGroup的對象
layout_content = (FrameLayout) findViewById(R.id.layout_content); main_radio = (RadioGroup) findViewById(R.id.main_radio);
2、設置選中RadioGroup中按鈕狀態發生改變的時候的事件監聽(當前選中按鈕變化的時候調用的方法)
//選中按鈕狀態發生改變的時候的事件監聽 main_radio.setOnCheckedChangeListener(new OnCheckedChangeListener() { //當前選中按鈕變化的時候調用的方法 @Override public void onCheckedChanged(RadioGroup group, int checkedId) { switch (checkedId) { case R.id.rb_function: index = 0; break; case R.id.rb_news_center: index = 1; break; case R.id.rb_smart_service: index = 2; break; case R.id.rb_gov_affairs: index = 3; break; case R.id.rb_setting: index = 4; break; }
3、接著就讓fragment的數據適配器(new出來)去讓fragment填充每個按鈕狀態的指向(把MainActivity繼承換成Fragment)
FragmentStatePagerAdapter fragmentStatePagerAdapter = new FragmentStatePagerAdapter(getSupportFragmentManager()) { @Override public int getCount() { return null; } //返回根據索引創建的fragment對象 @Override public Fragment getItem(int arg0) { return null; } };
4、實現適配器中的方法getCount和getItem的方法
FragmentStatePagerAdapter fragmentStatePagerAdapter = new FragmentStatePagerAdapter(getSupportFragmentManager()) { @Override public int getCount() { return 5; } //返回根據索引創建的fragment對象 @Override public Fragment getItem(int arg0) { BaseFragment baseFragment = null; switch (arg0) { case 0: baseFragment = new FunctionFragment(); break; case 1: baseFragment = new NewCenterFragment(); break; case 2: baseFragment = new SmartServiceFragment(); break; case 3: baseFragment = new GovAffairsFragment(); break; case 4: baseFragment = new SettingFragment(); break; } return baseFragment; } };
5、在getItem裡進行fragment的切換邏輯設計,並創建BaseFragment接口
6、(重要)技巧:為了讓以後的程序員在繼承BaseFragment時,不是讓BaseFragment去實現UI設計的邏輯,而是讓繼承的Fragment去實現UI設計的邏輯,所以就把BaseFragment設計成一個接口類,並提供接口給繼承它的Fragment使用 ,裡面還有一個加載數據的方法
BaseFragment.java public abstract class BaseFragment extends Fragment { private View view; @Override public void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); } //加載UI方法 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { view = initView(); return view; } @Override public void onActivityCreated(Bundle savedInstanceState) { initData(); super.onActivityCreated(savedInstanceState); } //加載數據方法 public abstract void initData(); //填充UI方法 public abstract View initView();
7、回到MainActivity裡的getItem方法,並實現第一個Fragment:FunctionFragment,讓FunctionFragment去實現設計UI的方法和填充數據的方法(initData+initUI),接著陸續實現其他的功能Fragment(新聞中心,智慧服務,政務指南,設置)
FunctionFragment.java public class FunctionFragment extends BaseFragment { @Override public void initData() { } @Override public View initView() { TextView textView = new TextView(getActivity()); textView.setText("首頁"); return textView; } }
8、回到第2步,在該方法裡是包括了第5步裡的getItem方法,並做Fragment替換操作,並提交事務(更新)
//fragment需要去替換幀布局內部內容的對象,獲取fragment的索引,也就是說根據case所獲取的index去拿到適配器裡的某一個Fragment Fragment fragment = (Fragment) fragmentStatePagerAdapter.instantiateItem(layout_content,index); //1,替換的幀布局對象,3,索引生成fragment對象 fragmentStatePagerAdapter.setPrimaryItem(layout_content, 0, fragment); //提交 fragmentStatePagerAdapter.finishUpdate(layout_content);
9、設置打開app的按鈕的默認選中:
//默認選中按鈕中按鈕的方法 main_radio.check(R.id.rb_function);
框架完成,測試
bug:fragment覆蓋重疊了
需求:我Fragment顯示,你Fragment隱藏
10、在BaseFragment裡實現SetMenuVisibility方法,控制界面fragment的顯示隱藏,如果當前界面顯示true,不顯示false
//控制界面的顯示隱藏,如果當前界面顯示true,不顯示false @Override public void setMenuVisibility(boolean menuVisible) { //獲取當前子類Fragment對應的view對象 if(getView()!=null){ getView().setVisibility(menuVisible?View.VISIBLE:View.GONE); } super.setMenuVisibility(menuVisible); }
測試:框架完成。
智慧北京主頁面的布局的第三種實現方式ViewPager+RadioGroup
考慮到左側側拉欄由於每個功能模塊都不一樣。我們就把其左側側拉去替換Fragment,減少耦合
項目zhbj_heima47演示(該項目是最終智慧北京的雛形)
把以前的項目中的res內容復制過來
把左側拉欄的library引入進來
1、在MainActivity的繼承改成SlidingFragmentActivity
public class MainActivity extends SlidingFragmentActivity {
2、設置內容顯示布局
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.content);
3、設置左側側拉欄的布局menu_frame
setBehindContentView(R.layout.menu_frame);
4、設置左側側拉欄樣式
SlidingMenu slidingMenu = getSlidingMenu(); slidingMenu.setBehindWidthRes(R.dimen.slidingmenu_offset); slidingMenu.setShadowDrawable(R.drawable.shadow); slidingMenu.setShadowWidthRes(R.dimen.shadow_width); slidingMenu.setMode(SlidingMenu.LEFT); slidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
5、讓左側側拉欄替換成MenuFragment:給Fragment設置BaseFragment(加載UI方法接口+加載數據填充UI的方法接口)
BaseFragment.java public abstract class BaseFragment extends Fragment { public View view; public Context context; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //考慮到繼承該BaseFragment的Fragment會使用到上下文對象。所以在這裡提前為子類准備好 context = getActivity(); } //加載UI 方法,xml---->view @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { view = initView(); return view; } //加載數據,填充UI的方法 @Override public void onActivityCreated(Bundle savedInstanceState) { initData(); super.onActivityCreated(savedInstanceState); } public abstract View initView() ; public abstract void initData() ; }
6、MenuFragment繼承BaseFragment,給其設計一個TextView
public class MenuFragment extends BaseFragment { private static final String tag = "MenuFragment"; private ListtitleList = new ArrayList (); @Override public View initView() { TextView textView = new TextView(context); textView.setText("左側側拉欄目"); return textView; } @Override public void initData() { } }
7、回到第5步,繼續讓左側側拉的對象替換該MenuFragment
//fragment替換左側側拉欄目 MenuFragment menuFragment = new MenuFragment(); getSupportFragmentManager() .beginTransaction() .replace(R.id.menu, menuFragment, "MENU") .commit();
框架搭建完畢,測試
在項目zhbj_heima47中實現新創建的HomeFragment的內容添加進去
頂部是ViewPager,底部是RadioGroup
實現步驟(搭建框架)
HomeFragment
1、在initView裡填充frag_home
@Override public View initView() { view = View.inflate(context, R.layout.frag_home, null); return view; } frag_home.xml
2、通過XUtil去把ViewPager對象和RadioGroup對象關聯進來
@ViewInject(R.id.layout_content) private MyViewPager layout_content; @ViewInject(R.id.main_radio) private RadioGroup main_radio; @Override public View initView() { view = View.inflate(context, R.layout.frag_home, null); //xutil,注解的方式找到控件 ViewUtils.inject(this, view); return view; } 自定義控件: MyViewPager.java /** * 禁止viewpager滑動 * * @author Administrator * */ //LazyViewPager屏蔽ViewPager預加載操作的類 public class MyViewPager extends LazyViewPager { private boolean HAS_TOUCH_MODE = false; public MyViewPager(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub } public MyViewPager(Context context) { super(context); } //向內部控件傳遞 @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return false; } //不響應事件 @Override public boolean onTouchEvent(MotionEvent ev) { return false; } }
3、實現initData方法,給ViewPager去填充內容,MyPagerAdapter方法
4、initData方法實現每個按鈕點擊要切換的Pager,讓ArrayList存儲BasePager的實現類,設計BasePager
@Override public void initData() { pagerList = new ArrayList();
5、BasePager,給繼承類設計初始化UI接口,初始化數據接口、還有構造方法裡把上下文對象傳進來。在構造方法裡調用初始化UI接口並把View對象返回給BasePager裡的View對象,並設計getRootView方法返回繼承者實現UI後的view對象
public abstract class BasePager { public Context context; private View view; //構建UI的方法 public abstract View initView(); //填充數據的方法 public abstract void initData(); public BasePager(Context context) { this.context = context; //當前的view其實就是頁面的展示效果 view = initView(); } //返回當前頁面樣式的方法 public View getRootView(){ return view; }
6、回到HomeFragment裡繼續實現initData方法,給ArrayList添加ViewPager對象,添加同時把ViewPager對象都創建出來(功能頁,新聞中心,智慧服務,政務指南,設置等)FunctionPager/NewCenterPager/SmartServicePager/GovAffairPager/SettingPager
@Override public void initData() { pagerList = new ArrayList(); pagerList.add(new FunctionPager(context)); pagerList.add(new NewCenterPager(context)); pagerList.add(new SmartServicePager(context)); pagerList.add(new GovAffarisPager(context)); pagerList.add(new SettingPager(context)); FunctionPager.java public class FunctionPager extends BasePager { public FunctionPager(Context context) { super(context); } @Override public View initView() { TextView textView = new TextView(context); textView.setText("首頁"); return textView; } @Override public void initData() { // TODO Auto-generated method stub } }
目前的框架搭建好了,每個Pager頁面之間都是獨立的,5個模塊,可以分發給5個人去干了,該框架的耦合性為0.
7、繼續實現MyPagerAdapter裡的邏輯方法
class MyPagerAdatper extends PagerAdapter{ @Override public int getCount() { return pagerList.size(); } @Override public boolean isViewFromObject(View arg0, Object arg1) { return arg0 == arg1; } @Override public Object instantiateItem(ViewGroup container, int position) { Log.i(tag, "instantiateItem position = "+position); container.addView(pagerList.get(position).getRootView()); return pagerList.get(position).getRootView(); } @Override public void destroyItem(ViewGroup container, int position, Object object) { Log.i(tag, "destroyItem position = "+position); container.removeView((View)object); }
8、在iniData方法去給layout_content.setAdapter(viewpager的適配器設置)
layout_content.setAdapter(new MyPagerAdatper());
9、實現點擊底部按鈕實現Viewpager的切換,在HomeFragment的initData方法裡實現main_radio的點擊事件監聽
main_radio.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { switch (checkedId) { case R.id.rb_function: break; case R.id.rb_news_center: break; case R.id.rb_smart_service: break; case R.id.rb_gov_affairs: break; case R.id.rb_setting: break; } } });
10、在點擊裡指揮viewpager對象指向哪個VIewPager對象
main_radio.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { switch (checkedId) { case R.id.rb_function: layout_content.setCurrentItem(0); break; case R.id.rb_news_center: layout_content.setCurrentItem(1); break; case R.id.rb_smart_service: layout_content.setCurrentItem(2); break; case R.id.rb_gov_affairs: layout_content.setCurrentItem(3); break; case R.id.rb_setting: layout_content.setCurrentItem(4); break; } } });
11、同時main_radio設置一個默認的選中
//默認選中首頁 main_radio.check(R.id.rb_function);
12、最後在MainActivity裡把內容頁的布局替換成該HomeFragment的Fragment布局
HomeFragment homeFragment = new HomeFragment(); getSupportFragmentManager() .beginTransaction() .replace(R.id.content_frame, homeFragment, "HOME") .commit(); }
測試:大致功能搞定,但是ViewPager的預加載問題(對用戶體驗沒什麼差異,主要是為了優化項目),還有手動滑動界面切換時,ViewPager不能去滑動。修改這些bug
問題解決1:ViewPager的預加載問題:
在自定義控件MyViewPager的父親類LazyViewPager.java裡解決,把DefaultOffscreenPagerLimit該成0
問題解決2:手動滑動界面切換時,ViewPager不能去滑動:
在MyViewPager進行如下設置就oK
/** * 禁止viewpager滑動 * * @author Administrator * */ //LazyViewPager屏蔽ViewPager預加載操作的類 public class MyViewPager extends LazyViewPager { private boolean HAS_TOUCH_MODE = false; public MyViewPager(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub } public MyViewPager(Context context) { super(context); } //向內部控件傳遞 @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return false; } //不響應事件 @Override public boolean onTouchEvent(MotionEvent ev) { return false; } }
演示圖片和按鈕控件的事件的點擊響應的區別,即事件響應規則,從源碼View出發
新建ViewOnClickDemo演示
activity_main.xml
imageView的控件對象:
//按鈕點擊事件 imageView.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { /* * ACTION_DOWN 0 * ACTION_UP 1 * ACTION_MOVE 2 */ /* * false 響應一次 action_down dispatchTouchEvent 返回false 沒有響應所有的事件 * true 響應兩次 按下 抬起 dispatchTouchEvent 返回true ,捕獲了所有事件,消費 */ Log.i(tag, "ImageView event = "+event.getAction()); return false; } });
button的控件對象:
//按鈕點擊事件 button.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { Log.i(tag, "button event = "+event.getAction()); /* * false 響應兩次 按下抬起 dispatchTouchEvent 返回true 消費所有事件 * true 響應兩次 按下抬起 dispatchTouchEvent 返回 true 消費所有事件 */ return false; } });
結論:按鈕是一個特殊的控件
*ImageView控件的響應風格
觀察imageView的父類有沒有dispatchTouchEvent方法,歸溯到View類的源碼發現存在,觀察其源碼
public boolean dispatchTouchEvent(MotionEvent event) { ... //mOnTouchListener就是imageView調用setOnTouchListener傳遞進來的對象 //onTouch返回值結果由自己重寫onTouch決定的,如果返回值為true,那當前if條件滿足,直接返回true,即dispatchTouchEvent返回true // dispatchTouchEvent返回true,響應所有事件 // 如果onTouch返回的false,順序執行onTouchEvent(event) if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && mOnTouchListener.onTouch(this, event)) { return true; } return onTouchEvent(event); }
觀察結論發現在imageView裡return true時會讓dispatchTouchEvent也返回true,而當imageView裡return false時,View中的dispatchTouchEvent方法就執行return onTouchEvent(event),那麼會發生什麼事情呢?
public boolean onTouchEvent(MotionEvent event) { ... //當前控件是否可以被點擊,如果可以被點擊進入if,返回true,即dispatchTouchEvent(ev)返回true,響應所有事件 //不可以被點擊,不進入if,直接返回false,即dispatchTouchEvent(ev)返回false,不響應所有事件 if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { switch (event.getAction()) { case MotionEvent.ACTION_UP: ... //點擊事件的調用方式,就是回調方法 performClick(); ... return true; } return false; }
發現View裡的源碼onTouchEvent(event)方法最後對於imageView是返回一個false,最後就是imageVIew在return false時的現象(沒有響應事件)
*button控件的響應風格
同樣研究button:觀察button的父類有沒有dispatchTouchEvent方法,歸溯到View類的源碼發現存在,之後的研究邏輯和ImageView一樣,唯一不同就是在button的點擊事件裡return false 時,View裡的源碼onTouchEvent(event)方法最後對於button是返回一個true。
經過以上的研究,就可以明白我們ViewOnClickDemo所發生的現象了
*那麼我們需求是,讓imageView的點擊事件可以和button的風格保持一致。
我們通過imageView.setOnclickListener來對ImageView一個對應的點擊事件
設置過ImageView的點擊事件後,點擊的風格就和button一樣了
//按鈕點擊事件 imageView.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { /* * ACTION_DOWN 0 * ACTION_UP 1 * ACTION_MOVE 2 */ /* * false 響應一次 action_down dispatchTouchEvent 返回false 沒有響應所有的事件 * true 響應兩次 按下 抬起 dispatchTouchEvent 返回true ,捕獲了所有事件,消費 */ Log.i(tag, "ImageView event = "+event.getAction()); return false; } }); //設置一個點擊事件 imageView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //添加上點擊事件後,onTouch返回false 響應兩次次 //action_down dispatchTouchEvent 返回false 沒有響應所有的事件 } });
*現在我們再給button也設置一下點擊的響應事件OnClickListener 在它的OnTouchListener設置了的同時並返回true時,其點擊的響應事件沒響應
如果把button的OnTouchListener設置了的同時並返回false時,會怎樣?就會調用button裡OnClickListener裡onClick的方法內容(原因主要是在View源碼裡的dispatchTouchEvent方法有線索,其內部主要是最後還是調用了一個Action_Up的方法,也就是OnClickListener裡onClick的回調方法的內容)
//按鈕點擊事件 button.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { Log.i(tag, "button event = "+event.getAction()); /* * false 響應兩次 按下抬起 dispatchTouchEvent 返回true 消費所有事件 * true 響應兩次 按下抬起 dispatchTouchEvent 返回 true 消費所有事件 */ return false; } }); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Log.i(tag, "button onclick......................."); //onTouch 返回true ,點擊事件不會響應 //onTouch 返回false ,點擊事件會響應 } }); //dispatchTouchEvent(ev)
ViewGroup案例的實現
項目ViewGroupClickDemo演示
在其activity_main.xml裡的自定義控件放入一個button控件
在自定義控件裡重寫攔截點擊事件的方法onInterceptTouchEvent(MotionEvent ev)通過修改其return的布爾值
在MainActivity裡去測試button的點擊事件和layout的點擊事件
對其結論進行研究
觀察其自定義控件的父類源碼ViewGroup中的dispatchTouchEvent的方法,
其可以發現,當onInterceptTouchEvent(MotionEvent ev)為true時執行if裡面的內容(內容一定是處理其子控件的點擊事件),false就不執行
圖片解釋其源碼的操作:
回到項目zhbj_heima47中處理問題2
讓MyViewPager繼承MyLazyViewPager(來解除預加載和屏蔽ViewPager的左右滑動)
並實現了其兩個方法,屏蔽其點擊事件
讓frag_home.xml裡調用MyViewPager自定義控件
在HomeFragment實現
測試:預加載和手勢滑動是否屏蔽了,測試成功
回到智慧北京項目:zhbj_heima47中繼續實現點擊新聞中心請求網絡
1、給HomeFragment中的layout_content(ViewPager對象)設置一個Listener,PagerChange,去獲取當前頁面對象(新聞中心),並調用新聞中心的ViewPager對象中的initData的方法去初始化數據(重點)
layout_content.setOnPageChangeListener(new OnPageChangeListener() { @Override public void onPageSelected(int position) { //請求網絡數據,填充UI的操作 pagerList.get(position).initData(); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageScrollStateChanged(int state) { } });
2、給NewCenterPager類(新聞中心欄目)去實現initData方法,就是在方法裡請求網絡,使用XUtils
@Override public void initData() { //請求網絡數據,xutil請求網絡 getData(); }
3、HMApi.java的接口用來配合網絡請求,並想辦法讓鏈接地址加上Test內容,就是測試的鏈接地址,我們需要動態的加,看HMApi怎麼做
public class HMApi { public final static String BASE_URL = "http://192.168.1.100:8080/zhbj"; public final static String NEWS_CENTER_CATEGORIES = BASE_URL + "/categories.json"; public static final String PHOTOS_URL = BASE_URL + "/photos/photos_1.json"; }
技巧:碰到一個接口類時,我們可以通過ctrl+T來查看其實現類
4、學會RequestParams的幾種方式去傳遞參數
// RequestParams requestParams = new RequestParams(); // requestParams.addBodyParameter("name", "ooxx"); // RequestParams requestParams = new RequestParams(); //// requestParams.ad // ListarrayList = new ArrayList (); // // BasicNameValuePair basicNameValuePair = new BasicNameValuePair("name", "ooxx"); // BasicNameValuePair basicNameValuePair1 = new BasicNameValuePair("password", "ooxx"); // BasicNameValuePair basicNameValuePair2 = new BasicNameValuePair("id", "ooxx"); // // arrayList.add(basicNameValuePair); // arrayList.add(basicNameValuePair1); // arrayList.add(basicNameValuePair2); // // requestParams.addBodyParameter(arrayList);
5、在httpUtils.send裡的一個參數實現其參數的匿名類,就是請求返回的方法onSuccess,onFailure
private void getData() { requestData(HttpMethod.GET, HMApi.NEWS_CENTER_CATEGORIES, null,new RequestCallBack() { @Override public void onSuccess(ResponseInfo responseInfo) { //請求成功的方法 Log.i(tag, responseInfo.result); } @Override public void onFailure(HttpException error, String msg) { //請求失敗的方法 } }); }
6、接著就是點擊按鈕去切換新聞中心去測試,注意測試前要在清單文件裡設置請求網絡的權限,json訪問成功
7、使用HiJson去查看訪問到的json結構
8、優化initData代碼:把請求網絡的代碼抽取出方法來放入到父類當中:requestData,在子類中我們只要調用方法並傳參就OK,這樣有什麼好處?以後我們需要該一次編碼,只要在父類中改就OK,子類就沒必要修改了,並考慮到子類很多,這樣的修改效率明顯高很多
public abstract class BasePager { public Context context; private View view; //構建UI的方法 public abstract View initView(); //填充數據的方法 public abstract void initData(); public BasePager(Context context) { this.context = context; //當前的view其實就是頁面的展示效果 view = initView(); } //返回當前頁面樣式的方法 public View getRootView(){ return view; } //將請求網絡的操作抽取到父類中 public void requestData(HttpMethod httpMethod,String url, RequestParams params,RequestCallBackcallBack) { // RequestParams requestParams = new RequestParams(); // requestParams.addBodyParameter("name", "ooxx"); // RequestParams requestParams = new RequestParams(); //// requestParams.ad // List arrayList = new ArrayList (); // // BasicNameValuePair basicNameValuePair = new BasicNameValuePair("name", "ooxx"); // BasicNameValuePair basicNameValuePair1 = new BasicNameValuePair("password", "ooxx"); // BasicNameValuePair basicNameValuePair2 = new BasicNameValuePair("id", "ooxx"); // // arrayList.add(basicNameValuePair); // arrayList.add(basicNameValuePair1); // arrayList.add(basicNameValuePair2); // // requestParams.addBodyParameter(arrayList); HttpUtils httpUtils = new HttpUtils(); httpUtils.send(httpMethod,url,params,callBack); } }
9、實現initData中的緩存的方法:就是為了二次訪問網絡時可以進行判斷後決定訪問緩存還是真正訪問網絡,存儲緩存的方法:1、文件(sp)2、數據庫 3、內存存儲,第三種不推薦
private void getData() { requestData(HttpMethod.GET, HMApi.NEWS_CENTER_CATEGORIES, null,new RequestCallBack() { @Override public void onSuccess(ResponseInfo responseInfo) { //請求成功的方法 Log.i(tag, responseInfo.result); //存儲操作 1,文件(sp) 2,數據庫 3,內存存儲 //url作為key SharedPreferencesUtil.saveStringData(context,HMApi.NEWS_CENTER_CATEGORIES,responseInfo.result); processData(responseInfo.result); } @Override public void onFailure(HttpException error, String msg) { //請求失敗的方法 } }); }
10、把sp寫成一個工具類SharePreferenceUtils
public class SharedPreferencesUtil { private static String CONFIG = "config"; private static SharedPreferences sharedPreferences; //寫入 public static void saveStringData(Context context,String key,String value){ if(sharedPreferences==null){ sharedPreferences = context.getSharedPreferences(CONFIG, Context.MODE_PRIVATE); } sharedPreferences.edit().putString(key, value).commit(); } //讀出 public static String getStringData(Context context,String key,String defValue){ if(sharedPreferences==null){ sharedPreferences = context.getSharedPreferences(CONFIG, Context.MODE_PRIVATE); } return sharedPreferences.getString(key, defValue); } }
11、在initData使用該sp的工具類去做緩存處理,在sp配置時可以使用url來存儲sp的key,並把請求網絡的代碼再次抽取成getData方法,並在initData加判斷邏輯是否二次訪問
@Override public void initData() { //判斷一下當前本地是否有數據 String result = SharedPreferencesUtil.getStringData(context, HMApi.NEWS_CENTER_CATEGORIES, ""); if(!TextUtils.isEmpty(result)){ //有值,展示,json解析過程 processData(result); } //請求網絡數據,xutil請求網絡 getData(); }
private void getData() { requestData(HttpMethod.GET, HMApi.NEWS_CENTER_CATEGORIES, null,new RequestCallBack() { @Override public void onSuccess(ResponseInfo responseInfo) { //請求成功的方法 Log.i(tag, responseInfo.result); //存儲操作 1,文件(sp) 2,數據庫 3,內存存儲 //url作為key SharedPreferencesUtil.saveStringData(context,HMApi.NEWS_CENTER_CATEGORIES,responseInfo.result); processData(responseInfo.result); } @Override public void onFailure(HttpException error, String msg) { //請求失敗的方法 } }); }
12、在判斷裡和onSuccess裡加入解析json的方法,請求網絡的結果processData(result)
processData(responseInfo.result);
13、processData(result)的解析json邏輯:把源碼復制過來,這是開源的jar文件(在資料裡面提供),把json解析成相對的javaBean
//解析json的操作 private void processData(String result) { }
14、實現新聞中心的javaBean-->NewCenter,json中的數據和我們java中的集合最類似。在給json裡的復雜類型再設計出一個javaBean-->NewCenterItem,注意我們在javaBean裡定義的字段必須和json裡的數據一模一樣
public class NewCenter { public Listdata; public List extend; public String retcode; public class NewCenterItem{ public List children; public String id; public String title; public String type; public String url; public String url1; public String dayurl; public String excurl; public String weekurl; } public class Children{ public String id; public String title; public String type; public String url; } }
15、繼續回到13步裡去解析json
//解析json的操作 private void processData(String result) { //json解析 Gson gson = new Gson(); NewCenter newCenter = gson.fromJson(result, NewCenter.class); Log.i(tag, newCenter.data.get(0).children.get(0).url); }
測試:
(!!!一下幾步涉及到在別的類如何獲取Fragment的對象並傳數據)
16、把json的javaBean去顯示到左側側拉欄,在processData裡進行for循環拿到數據後存到titleList集合裡,該titleList需要給左側側拉欄條目MunuFragment對象去使用,現在問題是如何去獲取MenuFragment對象?
//解析json的操作 private void processData(String result) { //json解析 Gson gson = new Gson(); NewCenter newCenter = gson.fromJson(result, NewCenter.class); Log.i(tag, newCenter.data.get(0).children.get(0).url); titleList.clear(); for(int i=0;i17、記得我們在替換fragment時的replace方法裡有一個參數是"MENU",這就是為了提供給別的類去獲取,所以在MainActivity裡設置一個獲取MenuFragment對象的方法getMenuFragment
MainActivity.java //fragment替換左側側拉欄目 MenuFragment menuFragment = new MenuFragment(); getSupportFragmentManager() .beginTransaction() .replace(R.id.menu, menuFragment, "MENU") .commit(); MenuFragment.java public class MenuFragment extends BaseFragment { private static final String tag = "MenuFragment"; private ListtitleList = new ArrayList (); @Override public View initView() { TextView textView = new TextView(context); textView.setText("左側側拉欄目"); return textView; } @Override public void initData() { } public void initMenu(List titleList) { this.titleList = titleList; Log.i(tag, "titleList.size() = "+titleList.size()); } } 18、回到NewCenterPager中通過一直使用的context上下文對象,其實這個就是MainActivity對象,那麼就可以使用context去調用獲取MenuFragment對象的方法getMenuFragment(側拉欄對象),給該對象傳參數,給該對象類創建一個初始化欄目的方法initMenu(ListtitlesList)去初始化,把已經准備好了的titleList集合傳給它,並實現側拉欄展示
//解析json的操作 private void processData(String result) { //json解析 Gson gson = new Gson(); NewCenter newCenter = gson.fromJson(result, NewCenter.class); Log.i(tag, newCenter.data.get(0).children.get(0).url); titleList.clear(); for(int i=0;imainactivity對象?? ((MainActivity)context).getMenuFragment().initMenu(titleList); }
JSON的定義: 一種輕量級的數據交換格式,具有良好的可讀和便於快速編寫的特性。業內主流技術為其提供了完整的解決方案(有點類似於正則表達式 ,獲得了當今大部分語言的支持)
(1)使用數據庫mysql,腳本語言如下: /* 用戶表*/ CREATE TABLE `usertbl` ( `id` int(11) NOT NULL AUTO_
因項目緣故需重新定制SwitchButton,效果如下: 過程如下:1.圓角矩形的繪制2.字體繪制3.小圓繪制4.左右滑動動畫效果繪制代碼如下: package
本文實例講述了Android使用GridView展示圖片的方法。分享給大家供大家參考,具體如下:今天說說GridView的使用。所謂GvidView翻譯過來就是網格布局: