cogitoergosum
cogitoergosum

Reputation: 2471

Issue with onClick on TextView in a custom list adapter

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.

  1. The Activity sets up the MessageAdapter to the ListView.
  2. The adapter sets up the text views with list data correctly.
  3. The adapter also sets up a 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

Answers (2)

user1888162
user1888162

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

Simas
Simas

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

Related Questions