Reputation: 2471
I have written a custom adapter to load the list for my ListView. It works. Currently, I am interested in making it respond to click so that an email could be sent. I added the code to send email (like this) to my click listener.
This is from my custom adapter; the context passed to the constructor MessagesAdapter()
is getApplicationContext()
. (I did try with this
as suggested here; but, it didn't work.)
public MessagesAdapter(Context context, int textViewResourceId, ArrayList<String> fileRecords) {
super(context, textViewResourceId, fileRecords);
this.ctx = context;
this.l = fileRecords;
}
@SuppressLint("ViewHolder")
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) ctx
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.activity_messages_item, parent, false);
TextView hView = (TextView) rowView.findViewById(R.id.messageHeader);
TextView dView = (TextView) rowView.findViewById(R.id.messageDetail);
TextView fView = (TextView) rowView.findViewById(R.id.messageFooter);
String s = l.get(position);
String[] parts = s.split(";");
hView.setText(parts[0]);
rm.setmHeader(parts[0]);
hView.setTextSize(TypedValue.COMPLEX_UNIT_SP, ctx.getResources().getDimension(R.dimen.header_text));
dView.setText(parts[1]);
rm.setmDetail(parts[1]);
hView.setTextSize(TypedValue.COMPLEX_UNIT_SP, ctx.getResources().getDimension(R.dimen.detail_text));
fView.setText(parts[2]);
rm.setmFooter(parts[2]);
hView.setTextSize(TypedValue.COMPLEX_UNIT_SP, ctx.getResources().getDimension(R.dimen.footer_text));
dView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Intent.EXTRA_EMAIL, rm.getmDetail().split(" ")[1]);
intent.putExtra(Intent.EXTRA_SUBJECT, ctx.getResources().getString(R.string.email_subject) + " " + rm.getmHeader());
ctx.startActivity(Intent.createChooser(intent, ""));
}
});
return rowView;
}
My problem seems to be due to startActivity()
. If don't use ctx.startActivity()
, I get a null pointer exception. If I do, I get a Android Runtime Exception. This is even with Intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.
07-27 02:50:15.747: E/AndroidRuntime(2092): android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
So, here it is.
MessageAdapter
to the ListView
. View.OnClickListener()
.When the displayed message is clicked, the application crashes.
Could you please help me how to implement the click ?
Layout for individual items
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="match_parent"
tools:context="com.foo.bar.MessageActivity" >
<TextView
android:id="@+id/messageHeader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:textStyle="bold"/>
<TextView
android:id="@+id/messageDetail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/messageHeader"
android:autoLink="email"/>
<TextView
android:id="@+id/messageFooter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/messageDetail" />
</RelativeLayout>
Layout for list view:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.foo.bar.MessagesActivity" >
<ListView
android:id="@android:id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true" >
</ListView>
</RelativeLayout>
Message adapter code
import java.util.ArrayList;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
public class MessagesAdapter extends ArrayAdapter<String> {
private Context ctx;
private ArrayList<String> l;
ReceivedMessages rm = new ReceivedMessages();
public MessagesAdapter(Context context, int textViewResourceId, ArrayList<String> fileRecords) {
super(context, textViewResourceId, fileRecords);
this.ctx = context;
this.l = fileRecords;
}
@SuppressLint("ViewHolder")
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) ctx
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.activity_messages_item, parent, false);
TextView hView = (TextView) rowView.findViewById(R.id.messageHeader);
TextView dView = (TextView) rowView.findViewById(R.id.messageDetail);
TextView fView = (TextView) rowView.findViewById(R.id.messageFooter);
String s = l.get(position);
String[] parts = s.split(";");
hView.setText(parts[0]);
hView.setTextSize(TypedValue.COMPLEX_UNIT_SP, ctx.getResources().getDimension(R.dimen.header_text));
hView.setClickable(false);
hView.setLongClickable(false);
hView.setOnClickListener(null);
dView.setText(parts[1]);
dView.setTextSize(TypedValue.COMPLEX_UNIT_SP, ctx.getResources().getDimension(R.dimen.detail_text));
dView.setClickable(false);
dView.setLongClickable(false);
fView.setText(parts[2]);
fView.setTextSize(TypedValue.COMPLEX_UNIT_SP, ctx.getResources().getDimension(R.dimen.footer_text));
fView.setClickable(false);
dView.setOnClickListener(null);
return rowView;
}
}
Using the MessageAdapter
// Handler for received Intents
private BroadcastReceiver fileReadReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
populateList();
}
};
private void populateList() {
String[] values = fc.getMessageArray();
final ArrayList<String> list = new ArrayList<String>();
for (int i = 0; i < values.length; i++) {
list.add(values[values.length-1-i]); // Reversing the list to show recent messages first
++i;
}
MessagesAdapter ma = new MessagesAdapter(getApplicationContext(),
R.layout.activity_messages_item, list);
final ListView listView = (ListView) findViewById(android.R.id.list);
listView.setClickable(true);
listView.setAdapter(ma);
listView.setOnItemClickListener(new ListView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
String s = list.get(Long.valueOf(id).intValue());
Log.d("MessagesActivity", s);
Intent emailintent = new Intent(android.content.Intent.ACTION_SEND);
emailintent.setType("plain/text");
emailintent.putExtra(android.content.Intent.EXTRA_EMAIL,new String[] {"[email protected]" });
emailintent.putExtra(android.content.Intent.EXTRA_SUBJECT, "");
emailintent.putExtra(android.content.Intent.EXTRA_TEXT,"");
}
}) ;
}
Upvotes: 0
Views: 2060
Reputation: 1735
You can also create clickable text by using android.text.SpannableString
and android.text.style.URLSpan
You can also make individual word or part of text clickable in same TextView with different actions. I made this example for you:
MainActivity:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getFragmentManager().beginTransaction().add(R.id.container, new MainFragment()).commit();
}
@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;
}
}
MainFragment:
public class MainFragment extends Fragment{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View view = inflater.inflate(R.layout.fragment_main, null);
ListView list = (ListView)view.findViewById(R.id.listView1);
ArrayList<String> items = new ArrayList<String>();
for(int i = 0; i < 10; i++){
items.add("These both words are clickable: word1 word2");
}
ListAdapter adapter = new ListAdapter(getActivity(), items);
list.setAdapter(adapter);
return view;
}
private class ListAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private ArrayList<String> mListItems;
public ListAdapter(Context context, ArrayList<String> items){
mInflater = LayoutInflater.from(context);
mListItems = items;
}
@Override
public int getCount() {
return mListItems.size();
}
@Override
public Object getItem(int position) {
return mListItems.get(position);
}
@Override
public long getItemId(int position) {
return 0;
}
public Spannable setSpan(String text){
Spannable span = new SpannableString(text);
//Span the word that you want to be clickable, in this case we span two words
//These both words are clickable: word1 word2
//Span word1, this will also underline the word
span.setSpan(new SpanListener(text.substring(32, 37)), 32, 37, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
span.setSpan(new SpanListener(text.substring(38, 43)), 38, 43, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return span;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(convertView == null){
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.list_item, null);
holder.text = (TextView)convertView.findViewById(R.id.textView1);
convertView.setTag(holder);
} else{
holder = (ViewHolder)convertView.getTag();
}
holder.text.setText(setSpan(mListItems.get(position)));
holder.text.setMovementMethod(LinkMovementMethod.getInstance());
((ViewGroup)convertView).setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
return convertView;
}
}
static class ViewHolder {
TextView text;
}
public class SpanListener extends URLSpan {
String spanned_text;
public SpanListener(String text) {
super(text);
spanned_text = text;
}
public void onClick(View v){
DialogFragment dialog = new TextDialog();
Bundle bundle = new Bundle();
bundle.putString("text", spanned_text);
dialog.setArguments(bundle);
dialog.show(getFragmentManager(), "");
}
@Override
public void updateDrawState(TextPaint paint) {
super.updateDrawState(paint);
//Remove underline
paint.setUnderlineText(false);
}
}
public static class TextDialog extends DialogFragment implements OnClickListener {
@Override
public Dialog onCreateDialog(Bundle savedinstanceState){
final Context context = this.getActivity();
String text = "";
Bundle bundle = this.getArguments();
if(bundle != null){
text = bundle.getString("text");
}
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage("You clicked: " + text);
builder.setPositiveButton("OK", this);
return builder.create();
}
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
}
}
}
list_item.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" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp" />
fragment_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.clickabletextviewinsidelistview.MainActivity$PlaceholderFragment" >
<ListView
android:id="@+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
Result:
Upvotes: 4
Reputation: 44118
The culprit of this problem is probably android:autoLink="email"
together with android:layout_width="match_parent"
. This fills ListView's item with a big clickable TextView.
Furthermore it seems you were setting it as unclickable, so ultimately digging yourself a hole :-)
If your whole item of the ListView will be clickable, you needn't use the autoLink tag. So just remove that. Everything else seems fine. Hope this works for ya. Happy coding ! :-)
Upvotes: 0