【问题标题】:Issue with onClick on TextView in a custom list adapter自定义列表适配器中的 onClick TextView 问题
【发布时间】:2014-07-28 05:42:52
【问题描述】:

我编写了一个自定义适配器来为我的 ListView 加载列表。有用。目前,我有兴趣让它响应点击,以便发送电子邮件。我添加了将电子邮件(如this)发送到我的点击监听器的代码。

这是来自我的自定义适配器;传递给构造函数MessagesAdapter() 的上下文是getApplicationContext()。 (我确实按照here 的建议尝试了this;但是,它没有用。)

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;
}

我的问题似乎是由于startActivity()。如果不使用ctx.startActivity(),我会得到一个空指针异常。如果我这样做了,我会得到一个 Android 运行时异常。 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?

所以,就是这样。

  1. Activity 将MessageAdapter 设置为ListView
  2. 适配器使用列表数据正确设置文本视图。
  3. 适配器还设置了View.OnClickListener()

点击显示的消息时,应用程序崩溃。

你能帮我如何实现点击吗?

单个项目的布局

<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>

列表视图的布局:

<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>

消息适配器代码

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;
    }
}

使用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[] {"random@gmail.com" });
            emailintent.putExtra(android.content.Intent.EXTRA_SUBJECT, "");
            emailintent.putExtra(android.content.Intent.EXTRA_TEXT,"");
        }

    }) ;
}

【问题讨论】:

  • hmm...所以,我关注了“类似问题”并注意到这个one 与我的相似。然而,在我的例子中,列表中的每一项都是一组三个TextViews——页眉、细节和页脚。我怎么知道哪个被点击了?我确实在我的Activity 中尝试过this;但在 logcat 中什么都没有。
  • 那么意图选择器出现了吗?
  • @user3249477 不,什么都没有。
  • 我只是在想——谁应该是可点击的。 ListViewTextViews。我为两者都做了setClickable(true);但没有结果。要登录,我还尝试了list.get(Long.valueOf(id).intValue()),因为我可以访问底层列表,而id 来自回调方法。日志中没有任何内容。
  • 那么您希望选择器何时触发?单击特定列表的项目或单击特定文本视图时?

标签: android listview android-listview


【解决方案1】:

您还可以使用android.text.SpannableStringandroid.text.style.URLSpan 创建可点击文本。您还可以通过不同的操作在同一个TextView 中使单个单词或部分文本可点击。我为你做了这个例子:

主活动:

    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;
    }
}

主片段:

    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>

结果:

【讨论】:

  • 感谢@user1888162 提供详细示例。我将在下一个版本中使用它。只是好奇...您认为,如果列表项包含多个 TextView,您的示例是否可行?
  • @cogitoergosum 是的,您可以在一个列表项中放置任意数量的文本视图。
【解决方案2】:

这个问题的罪魁祸首可能是android:autoLink="email"android:layout_width="match_parent"。这会用一个大的可点击 TextView 填充 ListView 的项目。

此外,您似乎将其设置为不可点击,因此最终给自己挖了一个洞:-)

如果您的整个 ListView 项目都是可点击的,则无需使用 autoLink 标记。所以只需删除它。其他一切似乎都很好。希望这对你有用。快乐编码! :-)

【讨论】:

  • 是的,autoLink 似乎是个问题。对于多个文本视图,它仅在单击页脚文本时才起作用。不知道为什么。现在,我将合并所有并“和平相处”。 :D 谢谢@user3249477 的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-04
  • 1970-01-01
  • 1970-01-01
  • 2015-12-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多