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
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.
onPageStarted
: As the name suggests, this method gets invoked when the url loading starts.shouldOverrideUrlLoading
: This method is called whenever an internal link from an already loaded page is clicked. For API>24shouldOverrideUrlLoading(WebView view, String url)
is deprecated, useshouldOverrideUrlLoading(WebView view, WebResourceRequest request)
instead.onPageFinished
: When the url is loaded completely and successfully, this gets invokedonReceivedError
: When the url isn’t loaded, this method gets invoked.
To enable zoom controls on the webview we can invoke the following methods on the webView instance.
1 2 3 4 5 |
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.
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
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.
Android WebView Bookmarks Code
The code of the activity_main.xml
layout is given below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
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.
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.
Add the following permission to access internet in your AndroidManifest.xml
.
1 2 3 |
<uses-permission android:name="android.permission.INTERNET"/> |
The code for the activity_browser.xml
is given below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<?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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
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.
We display and hide the ProgressBar when the url is loading and completed respectively.
The menu is inflated from the browser.xml
file as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 |
<?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.
In the onOptionsItemSelected()
we store or remove the url from the SharedPreferences depending on whether it exists or not.
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.
invalidateOptionsMenu()
is used to redraw the menu in the Toolbar.
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()
.
The code for activity_bookmark.xml
is given below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
<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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
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.
SimpleAdapter is a built-in adapter for the ListView. Its useful to map static data to views defined in an XML file.
The layout for the ListView rows is given below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?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
.
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.
References: WebView, Simple Adapter