长按

时间:2015-09-15 11:45:50

标签: android

我试图制作一个简单的待办事项列表,您可以长按一个项目将其标记为“已完成”,在这种情况下,它将显示为灰色且删除线。

我首先处理删除线,并在此处找到了一些示例代码creating a strikethrough text in Android?。但是问题是setPaintFlags()方法似乎只在TextView上工作,而我列表中的项目是String。我无法将一个字符串转换为TextView,我在这里找到了一个解决方法,但显然我们非常劝阻它:Cast String to TextView。我也查了一下SpannableString,但它似乎不适用于不同长度的字符串。

所以我回到第一个方面 - 是否有可能实现我想做的事情?或者我是否必须以不同方式存储我的列表项?

相关代码:

public class MainActivity extends ActionBarActivity {
    private ArrayList<String> items;
    private ArrayAdapter<String> itemsAdapter;
    private ListView lvItems;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //Setting what the ListView will consist of
        lvItems = (ListView) findViewById(R.id.lvItems);

        readItems();
        itemsAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, items);
        lvItems.setAdapter(itemsAdapter);
        // Set up remove listener method call
        setupListViewListener();
    }

    //Attaches a long click listener to the listview
     private void setupListViewListener() {
        lvItems.setOnItemLongClickListener(
                new AdapterView.OnItemLongClickListener() {
                    @Override
                    public boolean onItemLongClick(AdapterView<?> adapter,
                                                   View item, int pos, long id) {


                        // Trying to make the onLongClick strikethrough the text 

                        String clickedItem = items.get(pos);
                        //What do I do here??

                        // Refresh the adapter
                        itemsAdapter.notifyDataSetChanged();
                        writeItems();
                        // Return true consumes the long click event (marks it handled)
                        return true;
                    }

                });
    }

1 个答案:

答案 0 :(得分:3)

让我们退后一步,考虑一下您的应用。您想要向用户显示作业列表。每个工作都有描述。每项工作都有两种可能的状态:&#39;已完成&#39;或者&#39;没有完成&#39;。

所以我想介绍一个课程&#39; Job&#39;

class Job
{
    private String    mDescription;
    private boolean   mDone;

    public Job(String description)
    {
         this.mDescription = description;
         this.mDone = false;
    }
    // ... generate the usual getters and setters here ;-)
    // especially:
    public boolean isDone()
    {
         return mIsDone;
    }
}

这样你的ArrayList&#39; items&#39;成为ArrayList< Job >。工作完成与否将与其描述一起存储。这很重要,因为您希望通过更改UI元素的外观向用户显示作业的当前状态,但您还需要在数据级别上跟踪作业的状态。

UI元素 - TextView - 将配置为向用户显示有关作业的信息。一条信息是描述。 TextView会将其存储为String。另一条信息是状态(已完成/未完成)。 TextView将(在您的应用中)通过设置删除标记并更改其颜色来存储它。

由于性能原因,ListView使用的元素少于数据列表(&#39; items&#39;)所包含的元素,因此您必须编写自定义适配器。为了简洁起见,我保持代码非常简单,但是值得花时间阅读View Holder模式:

让我们使用布局文件&#39; mytextviewlayout.xml&#39;对于列表行:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:orientation="vertical"
          android:layout_width="match_parent"
          android:layout_height="match_parent">

    <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:text="Medium Text"
    android:id="@+id/textView"/>
</LinearLayout>

现在适配器的代码如下所示:

编辑从ArrayAdapter更改为BaseAdapter并添加了一个视图持有者(请参阅注释):

public class MyAdapter extends BaseAdapter
{
private ArrayList<Job>         mDatalist;
private int                    mLayoutID;
private Activity               mCtx;

private MyAdapter(){} // the adapter won't work with the standard constructor

public MyAdapter(Activity context, int resource, ArrayList<Job> objects)
{
    super();
    mLayoutID = resource;
    mDatalist = objects;
    mCtx = context;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View rowView = convertView;

    if (rowView == null) {
        LayoutInflater inflater = mCtx.getLayoutInflater();
        rowView = inflater.inflate(mLayoutID, null);

        ViewHolder viewHolder = new ViewHolder();
        viewHolder.tvDescription = (TextView) rowView.findViewById(R.id.textView);

        rowView.setTag(viewHolder); 
    }

    ViewHolder vholder = (ViewHolder) rowView.getTag();

    TextView tvJob = vholder.tvDescription;
    Job myJob = mDatalist.get(position);

    tvJob.setText(myJob.getJobDescription());
    if (myJob.isDone())
    {
        // apply changes to TextView
        tvJob.setPaintFlags(tvJob.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
        tvJob.setTextColor(Color.GRAY);
    }
    else
    {
        // show TextView as usual
        tvJob.setPaintFlags(tvJob.getPaintFlags() &    (~Paint.STRIKE_THRU_TEXT_FLAG));
        tvJob.setTextColor(Color.BLACK); // or whatever is needed...
    }

    return rowView;
}


@Override
public int getCount()
{
    return mDatalist.size();
}

@Override
public Object getItem(int position)
{
    return mDatalist.get(position);
}

@Override
public long getItemId(int position)
{
    return position;
}

static class ViewHolder
{
    public TextView tvDescription;
}

}

由于更改了适配器,

在MainActivity中,您必须声明&#39;项目&#39;和&#39; itemsAdapter&#39;如下:

private ArrayList<Job> items;
private MyAdapter itemsAdapter;

...并且在你的'onCreate()&#39;方法,你写:

itemsAdapter = new MyAdapter<String>(this, R.layout.mytextviewlayout, items);

不要忘记更改&#39; readItems()&#39;和&#39; writeItems()&#39;方法因为&#39;项目&#39;现在是ArrayList< Job >

然后,最后,&#39; onItemLongClick()&#39;方法:

编辑使用&#39; parent.getItemAtPosition()&#39;而不是&#39; items.get()&#39;,请参阅评论

@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id)
{
     // items.get(position).setDone(true);

     Object o = parent.getItemAtPosition(position);

     if (o instanceof Job)
     {
         ((Job) o).setDone(true);
     }
     // and now indeed the data set has changed :)

     itemsAdapter.notifyDataSetChanged(); 
     writeItems();
     return true;
 }