Firstly, like a lot of other people, we decided we wanted to override our ListViewAdapter to make a custom layout. This in itself is not difficult, and there are many, many good tutorials out there on the internet. We followed this one and the supplementary article linked within. For anyone looking to start doing so, check it out. It's really the best way to start getting the best out of the provided layouts.
For our purposes and using what we had available, we needed to use an ASP file to generate the thumbnail for our textbook listing. We found out upon trying to load it directly to an ImageView that it is actually returning an entire HTML block. When attempting to strip the image URL from that block, we found out that it was simply the same URL we'd just visited. Without much recourse, and no way to get at the data ourselves (3rd party systems, hurray!) we resolved to use a WebView and cut our losses.
When you use a focusable/clickable object inside a custom ListView layout, you immediately run into a problem: they steal all input from the ListView's native onClick events. This is overidden easily by setting the focusable="true" attribute to focusable="false". Same goes for clickable="true". This works for everything except a WebView. Because WebViews are more complicated and listen for a lot more types of events and more inherent functionality, a simple attribute change won't do anything. It essentially leaves your entire ListView item dead in the water.
The solution? Handle the click event in the WebView.
This is a hack, as I've said. This does not restore the functionality to the parent ListView item. As of this posting, I haven't figured that much out. This will, however, handle the click when it happens to the WebView. You could replace your entire ListView item with a single, formatted WebView and this would work swimmingly, but for our purposes, that's more trouble than it's worth.
Onto the code!
Firstly, some XML layout goodness. At present, I'm stuck using nested LinearLayouts, as I'm having issues getting a RelativeLayout to play nice with aligning TextViews to the right of a WebView. I'll update if I manage to fix that issue.
It's pretty simple, just set it up as per your liking.
Now, when handling an onClick with a ListView item, what we're doing is pretty standard. For those who haven't gotten that far on their own, here's what we were initially doing:
bookListView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView parent, View view,
int position, long id) {
Intent myIntent = new Intent(view.getContext(), BookDetailApp.class);
//save serialized book object to pass to BookDetailApp
Bundle bBooks = new Bundle();
currentBook = aBooks.get(position);
bBooks.putSerializable("currentBook", currentBook);
myIntent.putExtras(bBooks);
startActivityForResult(myIntent, 0);
}
});
Nothing special, as I said. We set up an Intent, toss our book into it for the extended detail app and then start up our new Activity. Doing it this way, without anything in the layout that attempts to steal focus, clicking on any part of the ListView item will launch the new app.
Now, when handling everything in the WebView, you have to set things up inside a custom ArrayAdapter for your ListView. That is pretty standard and documented all over the internet, so I won't be getting into all that detail. What you want to add to is the overridden getView method. The bit that replicates the above functionality is put where you would initialize your WebView and goes as such:
// Get a handle to our current WebView
WebView wv = (WebView) row.findViewById(R.id.book_display);
// Load it up with the properly formatted URL
wv.loadUrl(url);
// Set up its onTouchListener
// This is essentially the same as an onClickListener
// It just listens for more different types of input
wv.setOnTouchListener(new View.OnTouchListener()
{
// The definition of your actual onClick-type method
public boolean onTouch(View v, MotionEvent event)
{
// Switching on what type of touch it was
switch (event.getAction())
{
// ACTION_UP and ACTION_DOWN together make up a click
// We're handling both to make sure we grab it
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_UP:
{
// All of this is just like the previous Intent handler
Intent myIntent = new Intent(v.getContext(), BookDetailApp.class);
//save serialized book object to pass to BookDetailApp
Bundle bBooks = new Bundle();
currentBook = aBooks.get(bookListView.getPositionForView((View) v.getParent()));
bBooks.putSerializable("currentBook", currentBook);
myIntent.putExtras(bBooks);
startActivityForResult(myIntent, 0);
// The code will pause here until you exit the new Activity
// It will then go back to what it was doing
// In our case, waiting for more input
break;
}
}
// Returning false means that we won't be handling any other input
// Any un-handled gestures are tossed out
return false;
}
});
Voila! The nuances are in the comments, if you have any further questions, feel free to drop me a comment!
This can also be used to launch a new Activity to bring up a larger version of the image or any number of other functionalities. It's really just the most basic stepping stone. As I said, this doesn't completely fix the problem, but it is a work-around for the meantime. If anyone knows how to better handle it, please share, there are dozens of us waiting with bated breath!
Until next time!
awesome.. But I want to pinch zoom IN/Out of my webview. How can i catch the pinch action of the webview as you have catched the ACTION_UP and ACTION down. thanks
ReplyDeleteThis article is good... but some posts say we should not use WebView inside ListView... what you suggest.
ReplyDeletepackage in.wptrafficanalyzer.listviewwithimagesandtext;
ReplyDeleteimport java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
public class MainActivity extends Activity {
// Array of strings storing country names
String[] countries = new String[] {
"India",
"Pakistan",
"Sri Lanka",
"China",
"Bangladesh",
"Nepal",
"Afghanistan",
"North Korea",
"South Korea",
"Japan"
};
// Array of integers points to images stored in /res/drawable-ldpi/
int[] flags = new int[]{
R.drawable.india,
R.drawable.pakistan,
R.drawable.srilanka,
R.drawable.china,
R.drawable.bangladesh,
R.drawable.nepal,
R.drawable.afghanistan,
R.drawable.nkorea,
R.drawable.skorea,
R.drawable.japan
};
// Array of strings to store currencies
String[] currency = new String[]{
"Indian Rupee",
"Pakistani Rupee",
"Sri Lankan Rupee",
"Renminbi",
"Bangladeshi Taka",
"Nepalese Rupee",
"Afghani",
"North Korean Won",
"South Korean Won",
"Japanese Yen"
};
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Each row in the list stores country name, currency and flag
List> aList = new ArrayList>();
for(int i=0;i<10;i++){
HashMap hm = new HashMap();
hm.put("txt", "Country : " + countries[i]);
hm.put("cur","Currency : " + currency[i]);
hm.put("flag", Integer.toString(flags[i]) );
aList.add(hm);
}
// Keys used in Hashmap
String[] from = { "flag","txt","cur" };
// Ids of views in listview_layout
int[] to = { R.id.flag,R.id.txt,R.id.cur};
// Instantiating an adapter to store each items
// R.layout.listview_layout defines the layout of each item
SimpleAdapter adapter = new SimpleAdapter(getBaseContext(), aList, R.layout.listview_layout, from, to);
// Getting a reference to listview of main.xml layout file
ListView listView = ( ListView ) findViewById(R.id.listview);
// Setting the adapter to the listView
}
}
Hello Dear,
My name is Amit Bhaliya i developing a simple andorid app but i have some problem in my app so if you can help me.i explain my problem is there i cant give the hyperlink in my code so.you can provide me help then send me your email id then i will send you my code.
Thanx
Amit Bhaliya
Android Developer
amit.potent@gmail.com
I have run into the same issue and I'm using the same Motion UP thing as you.
ReplyDeleteWhat I want to know now is how to handle long clicks... haha.
Hi Sir ,I am doing a one project in that project Listview is there I click the each row in listview coming the webpage in webview how to write code in this situation please give me answer
ReplyDeletethis is my code in listview
public class listview extends Activity {
public class Intent {
public Intent(Context context, Class class1) {
// TODO Auto-generated constructor stub
}
public Intent(OnItemClickListener onItemClickListener,
Class class1) {
// TODO Auto-generated constructor stub
}
public void putExtra(String string, Object parse) {
// TODO Auto-generated method stub
}
}
ListView listView;
WebView webview;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
// listView = (ListView)findViewById(R.id.listview);
// Get ListView object from xml
listView = (ListView) findViewById(R.id.listview);
// Defined Array values to show in ListView
String[] values = new String[] { "ekwttv",
"erteqaa",
"ecityArab",
"Ladybird",
"Bakeryhome",
"istore2door",
"Nami e University",
"Elly Markos",
"e Fashion",
"Chic Watch",
"Jack Azore Fashions",
"Towers Construction Services",
"Caravan Manufacturing & Suppliers",
"Nami al nami",
"Car Parking Sheds",
"Yweb",
"Tents Services",
"Real Estate Services ",
"qw8motors",
"Kuwait Gate"
};
ArrayAdapter adapter = new ArrayAdapter(this,
android.R.layout.simple_list_item_1, values);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new OnItemClickListener()
{
private Context context;
protected void onListItemClick(ListView l, View v, int position, long id)
{
// super.onListItemClick(l,v,position,id);
Intent in = new Intent(this,webview.class);
startActivity(in);
}
@Override
public void onItemClick(AdapterView arg0, View arg1, int arg2, long arg3) {
// TODO Auto-generated method stub
}
});
}
protected void startActivity(Intent in) {
// TODO Auto-generated method stub
}
}
Thank ypu sir ,