带有书签的Android WebView

To display a web page as the part of the application we use android WebView in our application. We’ve covered the basics of WebView here. In this tutorial, we’ll implement a loading ProgressBar with a WebView and also allow bookmarking URLs for later viewing. Let’s get started.

为了将网页显示为应用程序的一部分,我们在应用程序中使用android WebView。 我们已经在这里介绍了WebView的基础知识。 在本教程中,我们将使用WebView实现加载进度条 ,并允许为URL加书签以便以后查看。 让我们开始吧。

Android WebView (Android WebView)

Android WebView class is an extension of Android’s View class that allows you to display web pages as a part of your activity layout. To load an external page we invoke the method loadUrl(String url) on the WebView instance and pass in the url of the external page. The WebViewClient contains the following four important methods that are generally overridden.

Android WebView类是Android View类的扩展,它允许您将网页显示为活动布局的一部分。 要加载外部页面,我们在WebView实例上调用方法loadUrl(String url)并传入外部页面的url。 WebViewClient包含以下四个通常被覆盖的重要方法。

  1. onPageStarted : As the name suggests, this method gets invoked when the url loading starts.

    onPageStarted :顾名思义,此方法在开始加载URL时被调用。
  2. shouldOverrideUrlLoading : This method is called whenever an internal link from an already loaded page is clicked. For API>24 shouldOverrideUrlLoading(WebView view, String url) is deprecated, use shouldOverrideUrlLoading(WebView view, WebResourceRequest request) instead.

    shouldOverrideUrlLoading :每当单击来自已加载页面的内部链接时,都会调用此方法。 对于API> 24 shouldOverrideUrlLoading(WebView view, String url)不建议使用shouldOverrideUrlLoading(WebView view, WebResourceRequest request) shouldOverrideUrlLoading(WebView view, String url) ,请改用shouldOverrideUrlLoading(WebView view, WebResourceRequest request)
  3. onPageFinished : When the url is loaded completely and successfully, this gets invoked

    onPageFinished :当URL完全成功加载​​时,将调用此方法
  4. onReceivedError : When the url isn’t loaded, this method gets invoked.

    onReceivedError :未加载网址时,将调用此方法。

To enable zoom controls on the webview we can invoke the following methods on the webView instance.

为了在webview上启用缩放控件,我们可以在webView实例上调用以下方法。

webView.getSettings().setSupportZoom(true);
webView.getSettings().setBuiltInZoomControls(true); // allow pinch to zooom
webView.getSettings().setDisplayZoomControls(false); // disable the default zoom controls on the page

Let’s create an application that loads a web page whilst showing the ProgressBar. We’ll add a functionality that lets us bookmark a URL and save it in our SharedPreferences for later viewing.

让我们创建一个在显示ProgressBar的同时加载网页的应用程序。 我们将添加一项功能,使我们可以为URL添加书签并将其保存在SharedPreferences中,以供以后查看。

具有书签项目结构的Android WebView (Android WebView with Bookmark Project Structure)

We’ve selected the Activity type as Navigation Drawer.

我们已将“活动”类型选择为“ 导航抽屉”

Note: If you’ve updated your build tools to API 26 and are experiencing an error : “Failed to resolve com.android.support:appcompat-v7:26.0.1 “, you need to add Google’s Maven Repository in the build.gradle file as shown below:

注意:如果您已经将构建工具更新为API 26,并且遇到错误:“无法解析com.android.support:appcompat-v7:26.0.1 ”,则需要在build.gradle添加Google的Maven存储库文件如下图所示:

apply plugin: 'com.android.application'

allprojects {
    repositories {
        jcenter()
        maven {
            url "https://maven.google.com"
        }
    }
}


android {
    compileSdkVersion 26
    buildToolsVersion "26.0.1"
    defaultConfig {
        applicationId "com.journaldev.webviewwithbookmarks"
        minSdkVersion 16
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:26.0.1'
    compile 'com.android.support:design:26.0.1'
    compile 'com.google.code.gson:gson:2.7'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    testCompile 'junit:junit:4.12'
}

Note: The Gson library dependency for saving bookmarked urls in Shared Preferences is also added above.

注意:上面还添加了用于在“ 共享首选项”中保存书签网址的Gson库依赖项。

Android WebView书签代码 (Android WebView Bookmarks Code)

The code of the activity_main.xml layout is given below:

下面给出了activity_main.xml布局的代码:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

    <include
        layout="@layout/app_bar_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main"
        app:menu="@menu/activity_main_drawer" />

</android.support.v4.widget.DrawerLayout>

The code for the app_bar_main.xml layout is given below:

下面给出了app_bar_main.xml布局的代码:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.journaldev.webviewwithbookmarks.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

</android.support.design.widget.CoordinatorLayout>

The layout for the content_main.xml is given below. It contains a Button that’ll be used to launch another activity.

下面给出了content_main.xml的布局。 它包含一个将用于启动另一个活动的按钮。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.journaldev.webviewwithbookmarks.MainActivity"
    tools:showIn="@layout/app_bar_main">

    <Button
        android:id="@+id/btnLaunchWebsite"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="LAUNCH WEBSITE"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

The code for the MainActivity.java is given below:

MainActivity.java的代码如下:

package com.journaldev.webviewwithbookmarks;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.support.design.widget.NavigationView;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;

public class MainActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener {

    Button button;
    NavigationView navigationView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        DrawerLayout drawer = findViewById(R.id.drawer_layout);

        button = findViewById(R.id.btnLaunchWebsite);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                gotoBrowserActivity();
            }
        });


        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.addDrawerListener(toggle);
        toggle.syncState();

        navigationView = findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);
    }

    @Override
    public void onBackPressed() {
        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        // Handle navigation view item clicks here.
        int id = item.getItemId();

        if (id == R.id.nav_home) {
            navigationView.getMenu().getItem(0).setChecked(false);
        } else if (id == R.id.nav_bookmark) {
            navigationView.getMenu().getItem(1).setChecked(false);
            startActivity(new Intent(this, BookmarkActivity.class));
        }
        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }

    private void gotoBrowserActivity() {
        startActivity(new Intent(this, BrowserActivity.class));
    }
}

In the above code we’ve defined two menu options(the file resides inside the menu folder as activity_main_drawer.xml) inside the NavigationDrawer.

在上面的代码中,我们在NavigationDrawer中定义了两个菜单选项(文件位于菜单文件夹中,名称为activity_main_drawer.xml )。

Clicking the Button in the MainActivity.java would launch the BrowserActivity.java and clicking the Bookmark menu button would launch the BookmarkActivity.java that we’ll see shortly.

单击MainActivity.java的Button将启动BrowserActivity.java ,单击Bookmark菜单按钮将启动BookmarkActivity.java ,我们将很快看到。

Add the following permission to access internet in your AndroidManifest.xml.

AndroidManifest.xml添加以下访问互联网的权限。

<uses-permission android:name="android.permission.INTERNET"/>

The code for the activity_browser.xml is given below.

下面给出了activity_browser.xml的代码。

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    android:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white"
    android:fitsSystemWindows="true">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.widget.NestedScrollView xmlns:android="https://schemas.android.com/apk/res/android"
        xmlns:app="https://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fadeScrollbars="false"
        android:scrollbarFadeDuration="0"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">


        <WebView
            android:id="@+id/webView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />


    </android.support.v4.widget.NestedScrollView>

    <ProgressBar
        android:id="@+id/progressBar"
        style="@style/Widget.AppCompat.ProgressBar.Horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="-7dp"
        android:indeterminate="true"
        android:visibility="gone"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />


</android.support.design.widget.CoordinatorLayout>

The code for the BrowserActivity.java is given below:

下面给出了BrowserActivity.java的代码:

package com.journaldev.webviewwithbookmarks;

import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.util.ArrayList;

public class BrowserActivity extends AppCompatActivity {


    public static final String PREFERENCES = "PREFERENCES_NAME";
    public static final String WEB_LINKS = "links";
    public static final String WEB_TITLE = "title";

    WebView webView;
    private ProgressBar progressBar;
    String current_page_url = "https://www.wikipedia.com";
    CoordinatorLayout coordinatorLayout;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_browser);

        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setTitle("");
        toolbar.setNavigationIcon(R.drawable.ic_arrow_back);
        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onBackPressed();
            }
        });

        if (getIntent().getExtras() != null) {
            current_page_url = getIntent().getStringExtra("url");
        }

        webView = findViewById(R.id.webView);
        progressBar = findViewById(R.id.progressBar);
        webView.loadUrl(current_page_url);
        initWebView();

        coordinatorLayout = findViewById(R.id.main_content);
    }

    private void initWebView() {
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                super.onPageStarted(view, url, favicon);
                progressBar.setVisibility(View.VISIBLE);
                current_page_url = url;
                invalidateOptionsMenu();
            }

            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                webView.loadUrl(url);
                return true;
            }
            
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    webView.loadUrl(request.getUrl().toString());
                }
                return true;
            }

            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);
                progressBar.setVisibility(View.GONE);
                invalidateOptionsMenu();
            }

            @Override
            public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
                super.onReceivedError(view, request, error);
                progressBar.setVisibility(View.GONE);
                invalidateOptionsMenu();
            }
        });

        webView.getSettings().setLoadWithOverviewMode(true);
        webView.getSettings().setUseWideViewPort(true);
        webView.clearCache(true);
        webView.clearHistory();
        webView.getSettings().setJavaScriptEnabled(true);
        webView.setHorizontalScrollBarEnabled(true);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.browser, menu);

        SharedPreferences sharedPreferences = getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE);
        String links = sharedPreferences.getString(WEB_LINKS, null);

        if (links != null) {

            Gson gson = new Gson();
            ArrayList<String> linkList = gson.fromJson(links, new TypeToken<ArrayList<String>>() {
            }.getType());

            if (linkList.contains(current_page_url)) {
                menu.getItem(0).setIcon(R.drawable.ic_bookmark_black_24dp);
            } else {
                menu.getItem(0).setIcon(R.drawable.ic_bookmark_border_black_24dp);
            }
        } else {
            menu.getItem(0).setIcon(R.drawable.ic_bookmark_border_black_24dp);
        }
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        if (item.getItemId() == R.id.action_bookmark) {

            String message;

            SharedPreferences sharedPreferences = getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE);
            String jsonLink = sharedPreferences.getString(WEB_LINKS, null);
            String jsonTitle = sharedPreferences.getString(WEB_TITLE, null);


            if (jsonLink != null && jsonTitle != null) {

                Gson gson = new Gson();
                ArrayList<String> linkList = gson.fromJson(jsonLink, new TypeToken<ArrayList<String>>() {
                }.getType());

                ArrayList<String> titleList = gson.fromJson(jsonTitle, new TypeToken<ArrayList<String>>() {
                }.getType());

                if (linkList.contains(current_page_url)) {
                    linkList.remove(current_page_url);
                    titleList.remove(webView.getTitle().trim());
                    SharedPreferences.Editor editor = sharedPreferences.edit();
                    editor.putString(WEB_LINKS, new Gson().toJson(linkList));
                    editor.putString(WEB_TITLE, new Gson().toJson(titleList));
                    editor.apply();


                    message = "Bookmark Removed";

                } else {
                    linkList.add(current_page_url);
                    titleList.add(webView.getTitle().trim());
                    SharedPreferences.Editor editor = sharedPreferences.edit();
                    editor.putString(WEB_LINKS, new Gson().toJson(linkList));
                    editor.putString(WEB_TITLE, new Gson().toJson(titleList));
                    editor.apply();

                    message = "Bookmarked";
                }
            } else {

                ArrayList<String> linkList = new ArrayList<>();
                ArrayList<String> titleList = new ArrayList<>();
                linkList.add(current_page_url);
                titleList.add(webView.getTitle());
                SharedPreferences.Editor editor = sharedPreferences.edit();
                editor.putString(WEB_LINKS, new Gson().toJson(linkList));
                editor.putString(WEB_TITLE, new Gson().toJson(titleList));
                editor.apply();

                message = "Bookmarked";
            }

            Snackbar snackbar = Snackbar.make(coordinatorLayout, message, Snackbar.LENGTH_LONG);
            snackbar.show();

            invalidateOptionsMenu();
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onBackPressed() {
        if (webView.canGoBack()) {
            webView.goBack();
        } else {
            super.onBackPressed();
        }
    }

}

In the above code, we load the url https://www.wikipedia.com in the WebView.

在上面的代码中,我们在WebView中加载URL https://www.wikipedia.com

We display and hide the ProgressBar when the url is loading and completed respectively.

分别在加载和完成URL时显示和隐藏ProgressBar。

The menu is inflated from the browser.xml file as shown below.

菜单从browser.xml文件browser.xml ,如下所示。

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_bookmark"
        android:icon="@drawable/ic_bookmark_black_24dp"
        android:orderInCategory="100"
        android:title="BOOKMARK"
        app:showAsAction="always" />

</menu>

In the onCreateOptionsMenu() we check if the current_page_url already exists in our SharedPreferences or not. Depending on the outcome, we show the relevant bookmark menu icon.

onCreateOptionsMenu()我们检查current_page_url是否已存在于我们的SharedPreferences中。 根据结果​​,我们显示相关的书签菜单图标。

In the onOptionsItemSelected() we store or remove the url from the SharedPreferences depending on whether it exists or not.

onOptionsItemSelected()我们根据URL是否存在来存储或从SharedPreferences中删除该URL。

The SharedPreferences stores the ArrayList of links and the respective web page titles, in the form of Gson strings that’ll be eventually displayed in the BookmarkActivity.java which we’ll discuss below.

SharedPreferences以Gson字符串的形式存储链接的ArrayList和相应的网页标题,这些字符串最终将显示在BookmarkActivity.java ,我们将在下面进行讨论。

invalidateOptionsMenu() is used to redraw the menu in the Toolbar.

invalidateOptionsMenu()用于在工具栏中重新绘制菜单。

onBackPressed() is used to navigate back through the web page if the user had clicked any of the internal links in the WebView by checking and returning using canGoBack() and goBack().

如果用户通过使用canGoBack()goBack()进行检查并返回来canGoBack() WebView中的任何内部链接,则使用onBackPressed()来浏览网页。

The code for activity_bookmark.xml is given below.

下面给出了activity_bookmark.xml的代码。

<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:layout_alignParentTop="true"
        android:background="?attr/colorPrimary"
        app:popupTheme="@style/AppTheme.PopupOverlay" />

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipeToRefresh"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/toolbar"
        android:layout_margin="@dimen/fab_margin">

        <ListView
            android:id="@+id/listView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </android.support.v4.widget.SwipeRefreshLayout>

    <LinearLayout
        android:id="@+id/emptyList"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:gravity="center"
        android:orientation="vertical"
        android:visibility="gone">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:gravity="center"
            android:text="WHOOPS"
            android:textColor="#212121"
            android:textSize="20sp"
            android:textStyle="bold" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:gravity="center"
            android:text="There are no bookmarks at the moment"
            android:textColor="#212121" />


    </LinearLayout>


</RelativeLayout>

The code for the BookmarkActivity.java is given below.

下面给出了BookmarkActivity.java的代码。

package com.journaldev.webviewwithbookmarks;

import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.AdapterView;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import static com.journaldev.webviewwithbookmarks.BrowserActivity.PREFERENCES;
import static com.journaldev.webviewwithbookmarks.BrowserActivity.WEB_LINKS;
import static com.journaldev.webviewwithbookmarks.BrowserActivity.WEB_TITLE;

public class BookmarkActivity extends AppCompatActivity {

    ArrayList<HashMap<String, String>> listRowData;

    public static String TAG_TITLE = "title";
    public static String TAG_LINK = "link";

    ListView listView;
    ListAdapter adapter;
    LinearLayout linearLayout;
    SwipeRefreshLayout mSwipeRefreshLayout;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bookmark);

        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setTitle("BOOKMARKS");
        toolbar.setNavigationIcon(R.drawable.ic_arrow_back);
        toolbar.setTitleTextColor(getResources().getColor(android.R.color.white));
        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onBackPressed();
            }
        });

        listView = findViewById(R.id.listView);
        linearLayout = findViewById(R.id.emptyList);

        mSwipeRefreshLayout = findViewById(R.id.swipeToRefresh);
        mSwipeRefreshLayout.setColorSchemeResources(R.color.colorAccent);
        mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                new LoadBookmarks().execute();

            }
        });

        new LoadBookmarks().execute();
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

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

                Object o = listView.getAdapter().getItem(position);
                if (o instanceof Map) {
                    Map map = (Map) o;
                    Intent in = new Intent(BookmarkActivity.this, BrowserActivity.class);
                    in.putExtra("url", String.valueOf(map.get(TAG_LINK)));
                    startActivity(in);
                }


            }
        });

        listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
                Object o = listView.getAdapter().getItem(i);
                if (o instanceof Map) {
                    Map map = (Map) o;
                    deleteBookmark(String.valueOf(map.get(TAG_TITLE)), String.valueOf(map.get(TAG_LINK)));
                }

                return true;
            }
        });

    }

    private class LoadBookmarks extends AsyncTask<String, String, String> {


        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected String doInBackground(String... args) {
            // updating UI from Background Thread
            runOnUiThread(new Runnable() {
                public void run() {

                    SharedPreferences sharedPreferences = getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE);
                    String jsonLink = sharedPreferences.getString(WEB_LINKS, null);
                    String jsonTitle = sharedPreferences.getString(WEB_TITLE, null);
                    listRowData = new ArrayList<>();

                    if (jsonLink != null && jsonTitle != null) {

                        Gson gson = new Gson();
                        ArrayList<String> linkArray = gson.fromJson(jsonLink, new TypeToken<ArrayList<String>>() {
                        }.getType());

                        ArrayList<String> titleArray = gson.fromJson(jsonTitle, new TypeToken<ArrayList<String>>() {
                        }.getType());


                        for (int i = 0; i < linkArray.size(); i++) {
                            HashMap<String, String> map = new HashMap<>();

                            if (titleArray.get(i).length() == 0)
                                map.put(TAG_TITLE, "Bookmark " + (i + 1));
                            else
                                map.put(TAG_TITLE, titleArray.get(i));

                            map.put(TAG_LINK, linkArray.get(i));
                            listRowData.add(map);
                        }

                        adapter = new SimpleAdapter(BookmarkActivity.this,
                                listRowData, R.layout.bookmark_list_row,
                                new String[]{TAG_TITLE, TAG_LINK},
                                new int[]{R.id.title, R.id.link});

                        listView.setAdapter(adapter);
                    }

                    linearLayout.setVisibility(View.VISIBLE);
                    listView.setEmptyView(linearLayout);


                }
            });
            return null;
        }

        protected void onPostExecute(String args) {
            mSwipeRefreshLayout.setRefreshing(false);
        }

    }

    private void deleteBookmark(final String title, final String link) {

        new AlertDialog.Builder(this)
                .setTitle("DELETE")
                .setMessage("Confirm that you want to delete this bookmark?")
                .setPositiveButton("YES", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        SharedPreferences sharedPreferences = getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE);
                        String jsonLink = sharedPreferences.getString(WEB_LINKS, null);
                        String jsonTitle = sharedPreferences.getString(WEB_TITLE, null);


                        if (jsonLink != null && jsonTitle != null) {


                            Gson gson = new Gson();
                            ArrayList<String> linkArray = gson.fromJson(jsonLink, new TypeToken<ArrayList<String>>() {
                            }.getType());

                            ArrayList<String> titleArray = gson.fromJson(jsonTitle, new TypeToken<ArrayList<String>>() {
                            }.getType());


                            linkArray.remove(link);
                            titleArray.remove(title);


                            SharedPreferences.Editor editor = sharedPreferences.edit();
                            editor.putString(WEB_LINKS, new Gson().toJson(linkArray));
                            editor.putString(WEB_TITLE, new Gson().toJson(titleArray));
                            editor.apply();

                            new LoadBookmarks().execute();
                        }
                        dialogInterface.dismiss();
                    }
                }).setNegativeButton("NO", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                dialogInterface.dismiss();
            }
        }).show();
    }

}

In the above code we deserialise the strings from SharedPreferences using Gson and convert them into the respective links and titles ArrayList of Strings inside the AsyncTask LoadBookmarks.

在上面的代码中,我们使用Gson反序列化了SharedPreferences的字符串,并将它们转换为AsyncTask LoadBookmarks内部的相应链接和标题为ArrayList of Strings。

SimpleAdapter is a built-in adapter for the ListView. Its useful to map static data to views defined in an XML file.

SimpleAdapter是ListView的内置适配器。 将静态数据映射到XML文件中定义的视图很有用。

The layout for the ListView rows is given below:

ListView行的布局如下所示:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >

    <TextView
        android:id="@+id/title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ellipsize="end"
        android:maxLines="1"
        android:paddingBottom="2dp"
        android:paddingTop="4dp"
        android:textColor="#000"
        android:textSize="16sp" />


    <TextView
        android:id="@+id/link"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ellipsize="end"
        android:maxLines="1"
        android:layout_below="@+id/title"
        android:paddingBottom="4dp"
        android:paddingTop="2dp"
        android:textSize="14sp" />


</RelativeLayout>

setOnItemLongClickListener() is invoked to long press to delete a bookmark. Returning a false in it would call setOnItemClickListener() at the same time too, hence its recommended to return true.

调用setOnItemLongClickListener()可以长按删除书签。 在其中返回false也会同时调用setOnItemClickListener() ,因此建议返回true

The output that the application gives is shown below.

该应用程序给出的输出如下所示。

This brings an end to this tutorial. You can download the final Android WebViewWithBookmarks Project from the link below.

本教程到此结束。 您可以从下面的链接下载最终的Android WebViewWithBookmarks项目

References: WebView, Simple Adapter

参考: WebView简单适配器

翻译自: https://www.journaldev.com/15729/android-webview-bookmarks

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值