多個Frament內嵌套WebView以及返回鍵監聽
剛參加工作,越來越覺得大學時學的東西實在是少之又少……最近在工作中有一個項目需要做一個內嵌WebView的安卓“外殼”,裏邊的H5由別人開發,我需要做一個外殼來承載這些頁面,在這個過程中卻是經歷了無數的磨難,心塞啊。。好在在一週的努力學習下,終於成功的將這個“外殼”交了上去。
這個外殼是一個仿淘寶的主頁框架,裏邊可以放WebView之類的種種,還算是比較通用的,主要是UI和一些邏輯的框架,沒有JavaScript的數據交互部分,效果圖如下。有需要的小夥伴們來看看吧~
- 效果圖
下邊做一下筆記,也是第一次在CSDN上發表自己的收穫,還請來看的各位多提意見,哪裏寫的不對不清楚的也可說出來,只要能讓彼此提高,讓這篇文章有質量,讓後學者又得,在下就感激不盡。
注:本文討論的是在Eclipse上開發的,不過並不影響AS上的正常運行
—————————————-我是分割線————————————————–
正文開始
整體預覽
項目結構整體如下(沒有分層,只是簡單實現了本文所討論的功能)
同時需要爲項目設置聯網權限,在AndroidMainFest.xml中:
<uses-permission android:name="android.permission.INTERNET"/>
佈局文件
由於是要在Fragment中嵌套WebView,同時底部還要有幾個按鈕來控制切換Fragment,所以做了如下佈局。
- activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:weightSum="10">
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="9"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
android:layout_margin="5sp"
android:weightSum="5">
<LinearLayout
android:id="@+id/index_menu_1"
android:orientation="vertical"
android:layout_width="0sp"
android:layout_height="match_parent"
android:layout_weight="1">
<ImageView
android:id="@+id/index_menu_1_img"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/bt_bg_index"/>
</LinearLayout>
<LinearLayout
android:id="@+id/index_menu_2"
android:orientation="vertical"
android:layout_width="0sp"
android:layout_height="match_parent"
android:layout_weight="1">
<ImageView
android:id="@+id/index_menu_2_img"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/bt_bg_type"/>
</LinearLayout>
<LinearLayout
android:id="@+id/index_menu_3"
android:orientation="vertical"
android:layout_width="0sp"
android:layout_height="match_parent"
android:layout_weight="1">
<ImageView
android:id="@+id/index_menu_3_img"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/bt_bg_contact_us"/>
</LinearLayout>
<LinearLayout
android:id="@+id/index_menu_4"
android:orientation="vertical"
android:layout_width="0sp"
android:layout_height="match_parent"
android:layout_weight="1">
<ImageView
android:id="@+id/index_menu_4_img"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/bt_bg_shopping_cart"/>
</LinearLayout>
<LinearLayout
android:id="@+id/index_menu_5"
android:orientation="vertical"
android:layout_width="0sp"
android:layout_height="match_parent"
android:layout_weight="1">
<ImageView
android:id="@+id/index_menu_5_img"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/bt_bg_mine"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
有點冗長啊……確實是沒什麼,就是一個用來承載Fragment的FrameLayout和底部幾個按鈕而已。
- fragment1.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#000">
<WebView
android:id="@+id/webview1"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ProgressBar
android:id="@+id/progressbar1"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="3dip"
android:max="100"
android:progress="0"
android:visibility="gone"/>
</LinearLayout>
幾個fragment的佈局文件內容都是一樣的,都是一個WebView加一個ProgressBar。
Activity文件
- MainActivity.java
public class MainActivity extends FragmentActivity implements OnClickListener , BackHandledInterface{
private BaseFragment mBackHandedFragment;
private ImageView imageView1;
private ImageView imageView2;
private ImageView imageView3;
private ImageView imageView4;
private ImageView imageView5;
private String nowFragment = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
imageView1 = (ImageView) findViewById(R.id.index_menu_1_img);
imageView1.setOnClickListener(this);
imageView2 = (ImageView) findViewById(R.id.index_menu_2_img);
imageView2.setOnClickListener(this);
imageView3 = (ImageView) findViewById(R.id.index_menu_3_img);
imageView3.setOnClickListener(this);
imageView4 = (ImageView) findViewById(R.id.index_menu_4_img);
imageView4.setOnClickListener(this);
imageView5 = (ImageView) findViewById(R.id.index_menu_5_img);
imageView5.setOnClickListener(this);
//初始化主頁,默認顯示的是第一個頁面
FragmentManager manager = getSupportFragmentManager();
// Log.e("backPress","當前Fragment數量:"+manager.getBackStackEntryCount());
while(manager.getBackStackEntryCount()>1) {manager.popBackStack();}
Fragment1 fragment1 = new Fragment1();
changeFragment(fragment1);
}
@Override
public void onClick(View view) {
int id = view.getId();
switch (id) {
case R.id.index_menu_1_img:
Fragment1 fragment1 = new Fragment1();
changeFragment(fragment1);
break;
case R.id.index_menu_2_img:
Fragment2 fragment2 = new Fragment2();
changeFragment(fragment2);
// Log.e("Click","當前Fragment數量:"+getSupportFragmentManager().getBackStackEntryCount());
break;
case R.id.index_menu_3_img:
Fragment3 fragment3 = new Fragment3();
changeFragment(fragment3);
break;
case R.id.index_menu_4_img:
Fragment4 fragment4 = new Fragment4();
changeFragment(fragment4);
// player.start();
break;
case R.id.index_menu_5_img:
// Log.i("tag", "點擊了--3");
Fragment5 fragment5 = new Fragment5();
changeFragment(fragment5);
// player.stop();
break;
default:
break;
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK) {
if(mBackHandedFragment == null || !mBackHandedFragment.onBackPressed()){
//Fragment1/2/3/4/5 中的onBackPressed方法返回值就是來這裏進行判斷的
//判斷當前Fragment是否需要消費該事件,如果沒有Fragment消費纔會自己消費
backFragment();
}
return true;
}else {
return super.onKeyDown(keyCode, event);
}
}
@Override
public void setSelectedFragment(BaseFragment selectedFragment) {
this.mBackHandedFragment = selectedFragment;
}
/**
* 底部MenuUI的回退邏輯
* @return true:回退成功;false:回退棧元素不足
*/
@Override
public boolean backFragment() {
FragmentManager manager = getSupportFragmentManager();
// Log.e("backPress","當前Fragment數量:"+manager.getBackStackEntryCount());
if(manager.getBackStackEntryCount()<=1) {
//僅剩當前主頁,再按返回鍵則退出程序
finish();
return false;
}else {
BaseFragment nf = (BaseFragment)manager.findFragmentByTag(nowFragment);
flushMenuUI(nf.back);
manager.popBackStack();
setSelectedFragment((BaseFragment)manager.findFragmentByTag(nowFragment));
//按了返回鍵之後,最上層的Fragment已經替換,所以這裏也要重新設置一下,不然會執行之前BaseFragment子類的onBackPressed方法
return true;
}
}
/**
* 根據需要更新的Fragment設定的UI更新邏輯
* @param fragment 需要更新的Fragment
*/
private void changeFragment(BaseFragment fragment) {
String tagName = fragment.toString();
Log.e("changeFragment","點擊"+tagName);
if(tagName.equals(nowFragment)) {
//點擊的menu與當前顯示Fragment一致,重新加載當前Fragment
}else {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
// Log.e("changeFragment",tagName+" "+((manager.findFragmentByTag(tagName) != null)?"已存在":"不存在"));
if(manager.findFragmentByTag(tagName) == null) {
//backStack中沒有tagName,新增Fragment
transaction.add(R.id.fragment_container, fragment,tagName);
transaction.addToBackStack(tagName);
fragment.back = nowFragment;
}else {
//backStack中有此Fragment,直接回退至tagName
manager.popBackStack(tagName,0);
}
flushMenuUI(tagName);
transaction.commit();
}
}
public void flushMenuUI(String newFragment) {
// Log.e("flushMenuUI", "當前:"+nowFragment+" 新的:"+newFragment);
if(!nowFragment.equals(newFragment)) {
if(!nowFragment.equals("")) {
clearSelect();
}
setSelect(newFragment);
nowFragment = newFragment;
}
}
/**
* 重置已選menu
*/
public void clearSelect() {
// Log.e("clearSelect", nowFragment);
new Thread() {
public void run() {
if(nowFragment.equals("Fragment1")) {
imageView1.setImageResource(R.drawable.bt_bg_index);
}else if(nowFragment.equals("Fragment2")) {
imageView2.setImageResource(R.drawable.bt_bg_type);
}else if(nowFragment.equals("Fragment3")) {
imageView3.setImageResource(R.drawable.bt_bg_contact_us);
}else if(nowFragment.equals("Fragment4")) {
imageView4.setImageResource(R.drawable.bt_bg_shopping_cart);
}else if(nowFragment.equals("Fragment5")) {
imageView5.setImageResource(R.drawable.bt_bg_mine);
}
};
}.run();
//本來是直接更新了UI,但是不知道怎麼總會出現更新不了的情況,加了個線程莫名就好了。也不知道具體爲什麼……
}
/**
* 設置已選menu
*/
public void setSelect(final String newFragment) {
// Log.e("setSelect", newFragment);
new Thread() {
public void run() {
if(newFragment.equals("Fragment1")) {
imageView1.setImageResource(R.drawable.bt_bg_index_selected);
}else if(newFragment.equals("Fragment2")) {
imageView2.setImageResource(R.drawable.bt_bg_type_selected);
}else if(newFragment.equals("Fragment3")) {
imageView3.setImageResource(R.drawable.bt_bg_contact_us_selected);
}else if(newFragment.equals("Fragment4")) {
imageView4.setImageResource(R.drawable.bt_bg_shopping_cart_selected);
}else if(newFragment.equals("Fragment5")) {
imageView5.setImageResource(R.drawable.bt_bg_mine_selected);
}
};
}.run();
}
}
寫了非常多的註釋,博客裏,額。。。好像也沒啥好說的了。都在代碼裏了……下邊是Fragment1.java。
- Fragment1.java
public class Fragment1 extends BaseFragment {
private WebView webView1;
private WebSettings webSettings;
private ProgressBar progressBar1;
private View view;
private String url = "https://www.baidu.com/";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment1,container,false);
initView();
webSettings = webView1.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setUseWideViewPort(true);
webSettings.setLoadWithOverviewMode(true);
//設置了一些WebView的參數,包括能不能使用JavaScript之類的
webView1.setWebViewClient(new MyWebViewClient());
webView1.loadUrl(url);
return view;
}
private void initView() {
progressBar1= (ProgressBar) view.findViewById(R.id.progressbar1);//進度條
webView1 = (WebView) view.findViewById(R.id.webview1);
}
private class MyWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
@Override
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
}
@Override
public void onPageFinished(WebView view, String url) {//頁面加載完成
progressBar1.setVisibility(View.GONE);
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {//頁面開始加載
progressBar1.setVisibility(View.VISIBLE);
}
}
@Override
public boolean onBackPressed(){
if(webView1.canGoBack()){
webView1.goBack();
Log.v("onBackPressed", this.toString()+"webView.goBack()");
return true;
//當前Fragment內的WebView可以返回,WebView進行返回,將true返回值給載體MainActivity
}else{
Log.e("onBackPressed",this.toString());
return false;
//當前Fragment內的WebView已經無法返回,將false返回值給載體MainActivity
}
}
@Override
public String toString() {
return "Fragment1";
}
}
同樣,看代碼裏的註釋。剩下的幾個Fragmet*.java我只寫了一個Fragment2做測試用,內容基本與Fragment1.java一樣。
- BaseFragment.java(幾個Fragment的父類)
import android.os.Bundle;
import android.support.v4.app.Fragment;
public abstract class BaseFragment extends Fragment{
public String back;
//接口
protected BackHandledInterface mBackHandledInterface;
//返回鍵事件
public abstract boolean onBackPressed();
/**
* 所有繼承BackHandledFragment的子類都將在這個方法中實現物理Back鍵按下後的邏輯
* FragmentActivity捕捉到物理返回鍵點擊事件後會首先詢問Fragment是否消費該事件
* 如果沒有Fragment消息時FragmentActivity自己纔會消費該事件
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(!(getActivity() instanceof BackHandledInterface)){
throw new ClassCastException("Hosting Activity must implement BackHandledInterface");
}else{
this.mBackHandledInterface = (BackHandledInterface)getActivity();
}
}
@Override
public void onStart() {
super.onStart();
//在new一個當前fragment的子類時,設置綁定至承載體的返回棧棧頂
mBackHandledInterface.setSelectedFragment(this);
}
}
BaseFragment.java內主要定義了一些共有的,必須的一些參數和方法。老規矩,詳見代碼內註釋。
- BackHandledInterface.java(返回鍵接口)
public interface BackHandledInterface {
void setSelectedFragment(BaseFragment selectedFragment);
boolean backFragment();
}
代碼已經都貼到這裏了。完整的源碼包會在下面貼出來。
還請各位多多指教,有任何疑問或者觀點都可以在評論區留言,我會第一時間回覆並改正博客內的錯誤。也可以加我QQ(543844351)一起討論,加QQ實際的將驗證信息寫本博客題目名即可。
另:
- 因爲我也是現學現賣,在CSDN裏看了關於Fragment的文章和一些WebView的文章,所以如有雷同……額沒錯,就是學來的。在此向前輩們表示深深地謝意~
- 不過!請尊重勞動果實!轉載請註明出處https://blog.csdn.net/weixin_41957078/article/details/82221951
- 源碼下載鏈接:https://download.csdn.net/download/weixin_41957078/10637183