第三堂(1)为ListView元件建立自定画面

ListView是Android应用程式很常使用的画面元件,它可以显示多笔资料项目让使用者浏览、选择与执行后续的操作。目前完成的记事应用程式,只有把简单的文字资料设定给ListView元件使用,其实这个元件有非常多不同的用途,它可以显示比较复杂的资料项目,让使用者勾选和执行后续的功能。

这一章会加强ListView元件的使用,为它设计专用的画面,让一个项目可以显示比较多的资料:

AndroidTutorial5_03_01_01

为了让记事资料可以清楚的分类,所以在新增与修改记事加入设定颜色的功能:

AndroidTutorial5_03_01_02 AndroidTutorial5_03_01_03

在浏览记事资料的主画面,提供使用者勾选项目的功能,在未选择与已选择项目的状态,需要显示不同的功能表项目:

AndroidTutorial5_03_01_04 AndroidTutorial5_03_01_05

如果使用者选择记事项目,为应用程式加入删除记事的功能:

AndroidTutorial5_03_01_06

9-1 记事资料的封装

不论是开发一般Java或Android应用程式,应用程式的功能越写越多,程式码也会更复杂,一般的物件封装作法可以让程式码比较简洁一些。这个应用程式需要管理所有的记事资料,所以应该为应用程式新增一个封装记事资料的类别。因为希望可以为每一个记事资料加入颜色设定的功能,所以先建立一个封装颜色资料的类别。在“net.macdidi.myandroidtutorial”套件上按鼠标右键,选择“New -> Java Class”,在Create New Class对话框的Name输入“Colors”,Kind选择“Enum”后选择“OK”。参考下面的内容完成这个程式码:

package net.macdidi.myandroidtutorial;

import android.graphics.Color;

public enum Colors {

    LIGHTGREY("#D3D3D3"), BLUE("#33B5E5"), PURPLE("#AA66CC"), 
    GREEN("#99CC00"), ORANGE("#FFBB33"), RED("#FF4444");

    private String code;

    private Colors(String code) {
        this.code = code;
    }

    public String getCode() {
        return code;
    }

    public int parseColor() {
        return Color.parseColor(code);
    }

}

在“net.macdidi.myandroidtutorial”套件上按鼠标右键,选择“New -> Java Class”,在Create New Class对话框的Name输入“Item”后选择“OK”。参考下面的内容完成这个程式码:

package net.macdidi.myandroidtutorial;

import java.util.Date;
import java.util.Locale;

public class Item implements java.io.Serializable {

    // 编号、日期时间、颜色、标题、内容、档案名称、经纬度、修改、已选择
    private long id;
    private long datetime;
    private Colors color;
    private String title;
    private String content;
    private String fileName;
    private double latitude;
    private double longitude;
    private long lastModify;
    private boolean selected;

    public Item() {
        title = "";
        content = "";
        color = Colors.LIGHTGREY;
    }

    public Item(long id, long datetime, Colors color, String title,
            String content, String fileName, double latitude, double longitude,
            long lastModify) {
        this.id = id;
        this.datetime = datetime;
        this.color = color;
        this.title = title;
        this.content = content;
        this.fileName = fileName;
        this.latitude = latitude;
        this.longitude = longitude;
        this.lastModify = lastModify;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public long getDatetime() {
        return datetime;
    }

    // 装置区域的日期时间
    public String getLocaleDatetime() {
        return String.format(Locale.getDefault(), "%tF  %<tR", new Date(datetime));
    }   

    // 装置区域的日期
    public String getLocaleDate() {
        return String.format(Locale.getDefault(), "%tF", new Date(datetime));
    }

    // 装置区域的时间
    public String getLocaleTime() {
        return String.format(Locale.getDefault(), "%tR", new Date(datetime));
    }   

    public void setDatetime(long datetime) {
        this.datetime = datetime;
    }

    public Colors getColor() {
        return color;
    }

    public void setColor(Colors color) {
        this.color = color;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public double getLatitude() {
        return latitude;
    }

    public void setLatitude(double latitude) {
        this.latitude = latitude;
    }

    public double getLongitude() {
        return longitude;
    }

    public void setLongitude(double longitude) {
        this.longitude = longitude;
    }

    public long getLastModify() {
        return lastModify;
    }

    public void setLastModify(long lastModify) {
        this.lastModify = lastModify;
    }

    public boolean isSelected() {
        return selected;
    }

    public void setSelected(boolean selected) {
        this.selected = selected;
    }

}

为了让记事资料项目可以使用不同的颜色分类,所以新增一个绘图资源。在“res/drawable”目录上按鼠标右键,选择“New -> Drawable resource file”。在“File name”输入“item_drawable”后选择“OK”。参考下面的内容完成这个绘图资源:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >
 
    <corners 
        android:topLeftRadius="20sp"
        android:topRightRadius="20sp"
        android:bottomLeftRadius="20sp"
        android:bottomRightRadius="20sp" />
 
    <solid android:color="#AAAAAA"/>
 
</shape>

为了让ListView元件的每一个项目可以显示比较多的资料,你可以为项目建立一个画面配置档。这个画面配置档需要使用一个额外的图示(selected_icon.png),用来显示使用者已经选择一个项目,你可以在GitHub这一章的范例程式专案找到这个图档,把它复制到“res/drawable”目录。现在准备新增一个给ListView元件项目使用的画面资源,在“res/layout”目录上按鼠标右键,选择“New -> Layout resource file”。在“File name”输入“single_item”后选择“OK”。参考下面的内容完成这个画面资源:

<?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="horizontal" >
 
    <!-- 颜色分类 -->
    <RelativeLayout 
        android:id="@+id/type_color"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_margin="3sp"
        android:background="@drawable/item_drawable" >
 
        <!-- 勾选 -->
        <ImageView 
            android:id="@+id/selected_item"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_centerInParent="true"
            android:src="@drawable/selected_icon"
            android:visibility="invisible" />
 
    </RelativeLayout>
 
    <LinearLayout 
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="3sp"
        android:gravity="center_vertical"
        android:orientation="vertical" >
 
        <!-- 标题 -->
        <TextView 
            android:id="@+id/title_text"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:gravity="center_vertical" />
 
        <!-- 日期时间 -->
        <TextView 
            android:id="@+id/date_text"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:gravity="center_vertical" />
 
    </LinearLayout>
</LinearLayout>

需要在ListView元件中显示比较复杂的画面,就不能使用一般的Adapter物件,你可以依照自己的需求,撰写一个自定的Adapter类别给ListView元件使用。在“net.macdidi.myandroidtutorial”套件上按鼠标右键,选择“New -> Java Class”,在Create New Class对话框的Name输入“ItemAdapter”后选择“OK”。参考下面的内容完成这个程式码:

package net.macdidi.myandroidtutorial;

import java.util.List;

import android.content.Context;
import android.graphics.drawable.GradientDrawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

public class ItemAdapter extends ArrayAdapter {

    // 画面资源编号
    private int resource;
    // 包装的记事资料
    private List items;

    public ItemAdapter(Context context, int resource, List items) {
        super(context, resource, items);
        this.resource = resource;
        this.items = items;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        LinearLayout itemView;
        // 读取目前位置的记事物件
        final Item item = getItem(position);

        if (convertView == null) {
            // 建立项目画面元件
            itemView = new LinearLayout(getContext());
            String inflater = Context.LAYOUT_INFLATER_SERVICE;
            LayoutInflater li = (LayoutInflater)
                    getContext().getSystemService(inflater);
            li.inflate(resource, itemView, true);
        }
        else {
            itemView = (LinearLayout) convertView;
        }

        // 读取记事颜色、已选择、标题与日期时间元件
        RelativeLayout typeColor = (RelativeLayout) itemView.findViewById(R.id.type_color);
        ImageView selectedItem = (ImageView) itemView.findViewById(R.id.selected_item);
        TextView titleView = (TextView) itemView.findViewById(R.id.title_text);
        TextView dateView = (TextView) itemView.findViewById(R.id.date_text);

        // 设定记事颜色
        GradientDrawable background = (GradientDrawable)typeColor.getBackground();
        background.setColor(item.getColor().parseColor());

        // 设定标题与日期时间
        titleView.setText(item.getTitle());
        dateView.setText(item.getLocaleDatetime());

        // 设定是否已选择
        selectedItem.setVisibility(item.isSelected() ? View.VISIBLE : View.INVISIBLE);

        return itemView;
    }

    // 设定指定编号的记事资料
    public void set(int index, Item item) {
        if (index >= 0 && index < items.size()) {
            items.set(index, item);
            notifyDataSetChanged();
        }
    }

    // 读取指定编号的记事资料
    public Item get(int index) {
        return items.get(index);
    }

}

完成这些程式码与画面配置档以后,就完成基本的准备工作了。

9-2 使用自定画面的ListView元件

为了让ListView元件使用已经准备好的程式码与资源,之前已经写好的主画面元件,就要执行比较大幅度的修改。开启“net.macdidi.myandroidtutorial”套件下的“MainActivity.java”,修改字段变量的宣告:

private ListView item_list; 
private TextView show_app_name;

// 删除原来的宣告
//private ArrayList data = new ArrayList<>();
//private ArrayAdapter adapter;

// ListView使用的自定Adapter物件
private ItemAdapter itemAdapter;
// 储存所有记事本的List物件
private List items;

// 选单项目物件
private MenuItem add_item, search_item, revert_item, share_item, delete_item;

// 已选择项目数量
private int selectedCount = 0;

同样在“MainActivity.java”,参考下列的说明,修改“onCreate”方法的程式码:

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

    processViews();
    processControllers();

    // 删除原来的程式码
    //data.add("关于Android Tutorial的事情");
    //data.add("一只非常可爱的小狗狗!");
    //data.add("一首非常好听的音乐!");

    //int layoutId = android.R.layout.simple_list_item_1;
    //adapter = new ArrayAdapter(this, layoutId, data);
    //item_list.setAdapter(adapter);        

    // 加入范例资料       
    items = new ArrayList();

    items.add(new Item(1, new Date().getTime(), Colors.RED, "关于Android Tutorial的事情.", "Hello content", "", 0, 0, 0));
    items.add(new Item(2, new Date().getTime(), Colors.BLUE, "一只非常可爱的小狗狗!", "她的名字叫“大热狗”,又叫
作“奶嘴”,是一只非常可爱
的小狗。", "", 0, 0, 0));
    items.add(new Item(3, new Date().getTime(), Colors.GREEN, "一首非常好听的音乐!", "Hello content", "", 0, 0, 0));

    // 建立自定Adapter物件
    itemAdapter = new ItemAdapter(this, R.layout.single_item, items);
    item_list.setAdapter(itemAdapter);        
}

执行上面的修改以后,会发现这个程式码出现一些错误,这些错误会在“onActivityResult”与“processControllers”这两个方法里面,你可以参考下列的作法,先把这两的方法的所有程式码加上注解,后面再慢慢修改它们:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    /*
    ...
    */
}

private void processControllers() {
    /*
    ...
    */
}

使用上面介绍的方法处理程式码以后,错误的情况就会消失了,先执行这个应用程式,看看是否可以正常的显示应用程式画面。

9-3 新增记事的资料传送与接收

改用目前的方式处理记事资料以后,新增记事的作法就要执行一些必要的修改。开启“net.macdidi.myandroidtutorial”套件下的“ItemActivity.java”,加入这些新的字段变量宣告:

// 启动功能用的请求代码
private static final int START_CAMERA = 0;
private static final int START_RECORD = 1;
private static final int START_LOCATION = 2;
private static final int START_ALARM = 3;
private static final int START_COLOR = 4;

// 记事物件
private Item item;

同样在“ItemActivity.java”,参考下列的说明,修改“onCreate”方法的程式码:

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

    processViews();

    // 取得Intent物件
    Intent intent = getIntent();
    // 读取Action名称
    String action = intent.getAction();

    // 如果是修改记事
    if (action.equals("net.macdidi.myandroidtutorial.EDIT_ITEM")) {
        // 接收与设定记事标题
        String titleText = intent.getStringExtra("titleText");
        title_text.setText(titleText);
    }
    // 新增记事
    else {
        item = new Item();
    }
}

同样在“ItemActivity.java”,参考下列的说明,修改“onSubmit”方法的程式码,调整确认新增记事以后要执行的工作:

// 点击确定与取消按钮都会呼叫这个方法
public void onSubmit(View view) {
    // 确定按钮
    if (view.getId() == R.id.ok_teim) {
        // 读取使用者输入的标题与内容
        String titleText = title_text.getText().toString();
        String contentText = content_text.getText().toString();

        // 设定记事物件的标题与内容
        item.setTitle(titleText);
        item.setContent(contentText);

        // 如果是修改记事
        if (getIntent().getAction().equals(
                "net.macdidi.myandroidtutorial.EDIT_ITEM")) {
            item.setLastModify(new Date().getTime());
        }
        // 新增记事
        else {
            item.setDatetime(new Date().getTime());
        }

        Intent result = getIntent();
        // 设定回传的记事物件
        result.putExtra("net.macdidi.myandroidtutorial.Item", item);
        setResult(Activity.RESULT_OK, result);
    }

    // 结束
    finish();
}

回到“net.macdidi.myandroidtutorial”套件下的“MainActivity.java”,找到“onActivityResult”方法,移除之前加入的注解,参考下列的程式码修改新增记事后需要处理的工作。因为修改记事的部份还没有完成,所以先把它们设定为注解。

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // 如果被启动的Activity元件传回确定的结果
    if (resultCode == Activity.RESULT_OK) {
        // 读取记事物件
        Item item = (Item) data.getExtras().getSerializable(
                "net.macdidi.myandroidtutorial.Item");

        // 如果是新增记事
        if (requestCode == 0) {
            // 设定记事物件的编号与日期时间
            item.setId(items.size() + 1);
            item.setDatetime(new Date().getTime());

            // 加入新增的记事物件
            items.add(item);

            // 通知资料改变
            itemAdapter.notifyDataSetChanged();
        }
        /*
        // 如果是修改记事
        else if (requestCode == 1) {
            ...
        }
        */
    }
}

完成上面的工作以后,执行这个应用程式,测试新增记式资料的功能是否正确。

9-4 修改记事的资料传送与接收

完成新增记事功能以后,接下来处理工作比较多一些的修改记事功能。开启“net.macdidi.myandroidtutorial”套件下的“MainActivity.java”,找到“processControllers”方法,移除之前加入的注解。在这个方法中找到处理ListView项目长按事件的程式码,先把它们设定为注解:

/*
// 建立选单项目长按监听物件
OnItemLongClickListener itemLongListener = new OnItemLongClickListener() {
    ...
    }
};

// 注册选单项目长按监听物件
item_list.setOnItemLongClickListener(itemLongListener);
*/  

接下来参考下列的程式码,修改处理ListView项目点击事件的程式码:

    // 建立选单项目点击监听物件
    AdapterView.OnItemClickListener itemListener = new AdapterView.OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView parent, View view,
                                int position, long id) {
            // 读取选择的记事物件
            Item item = itemAdapter.getItem(position);

            Intent intent = new Intent(
                    "net.macdidi.myandroidtutorial.EDIT_ITEM");

            // 设定记事编号与记事物件
            intent.putExtra("position", position);
            intent.putExtra("net.macdidi.myandroidtutorial.Item", item);

            startActivityForResult(intent, 1);
        }
    };

    // 注册选单项目点击监听物件
    item_list.setOnItemClickListener(itemListener);

你可以注意到在点击一个记事项目以后,传送的资料已经修改为Item物件,所以修改记事元件也要执行对应的调整。开启“net.macdidi.myandroidtutorial”套件下的“ItemActivity.java”,修改“onCreate”方法里面的程式码:

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

    processViews();

    // 取得Intent物件
    Intent intent = getIntent();
    // 读取Action名称
    String action = intent.getAction();

    // 如果是修改记事
    if (action.equals("net.macdidi.myandroidtutorial.EDIT_ITEM")) {
        // 接收记事物件与设定标题、内容
        item = (Item) intent.getExtras().getSerializable(
                "net.macdidi.myandroidtutorial.Item");
        title_text.setText(item.getTitle());
        content_text.setText(item.getContent());
    }
    // 新增记事
    else {
        item = new Item();
    }
}

修改记事元件在使用者确认内容以后,回到主画面元件处理修改后的工作。开启“net.macdidi.myandroidtutorial”套件下的“MainActivity.java”,修改“onActivityResult”方法里面的程式码:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // 如果被启动的Activity元件传回确定的结果
    if (resultCode == Activity.RESULT_OK) {
        // 读取记事物件
        Item item = (Item) data.getExtras().getSerializable(
                "net.macdidi.myandroidtutorial.Item");

        // 如果是新增记事
        if (requestCode == 0) {
            ...
        }
        // 如果是修改记事
        else if (requestCode == 1) {
            // 读取记事编号
            int position = data.getIntExtra("position", -1);

            if (position != -1) {
                // 设定修改的记事物件
                items.set(position, item);
                itemAdapter.notifyDataSetChanged();
            }
        }
    }
}

完成修改记事功能的调整工作,执行应用程式,点选一笔记事项目,修改内容并确定以后,看看功能是否正确。

9-5 设定记事颜色

像记事这类应用程式,使用一段时间以后,通常会储存很多资料,为了让使用者可以清楚的分类与查询这些记事资料,所以为应用程式加入颜色分类的功能。使用者在新增或修改记事资料的时候,可以依照自己的需求为它设定一个颜色,为设定颜色的功能设计一个Activity元件,元件的名称是“ColorActivity.java”,画面配置档的名称是“activity_color.xml”。在最顶端的“app”目录按鼠标左键,选择“New -> Activity -> Blank Activity”,元件与画面配置档名称依照上面的规划。建立元件以后,开启应用程式设定档“AndroidManifest.xml”,参考下列的内容,加入对话框样式的设定:

<activity
    android:name="net.macdidi.myandroidtutorial.ColorActivity"
    android:theme="@android:style/Theme.Dialog"
    android:label="@string/title_activity_color" />

选择颜色功能的画面设计比较简单一些,开启在“res/layout”目录下的“activity_color.xml”,把它修改为下面的内容:

<HorizontalScrollView
    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:padding="6sp"
    android:spacing="3sp"
    tools:context="net.macdidi.myandroidtutorial.ColorActivity">
 
    <LinearLayout
        android:id="@+id/color_gallery"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal" />
 
</HorizontalScrollView>

开启在“net.macdidi.myandroidtutorial”套件下的“ColorActivity.java”,把它修改为下面的内容:

package net.macdidi.myandroidtutorial;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;

public class ColorActivity extends Activity {

    private LinearLayout color_gallery;

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

        processViews();

        ColorListener listener = new ColorListener();

        for (Colors c : Colors.values()) {
            Button button = new Button(this);
            button.setId(c.parseColor());
            LinearLayout.LayoutParams layout =
                    new LinearLayout.LayoutParams(128, 128);
            layout.setMargins(6, 6, 6, 6);
            button.setLayoutParams(layout);
            button.setBackgroundColor(c.parseColor());

            button.setOnClickListener(listener);

            color_gallery.addView(button);
        }
    }

    private void processViews() {
        color_gallery = (LinearLayout) findViewById(R.id.color_gallery);
    }

    private class ColorListener implements OnClickListener {

        @Override
        public void onClick(View view) {
            Intent result = getIntent();
            result.putExtra("colorId", view.getId());
            setResult(Activity.RESULT_OK, result);
            finish();
        }

    }

}

完成准备工作以后,就可以回到记事元件加入需要的程式码。开启在“net.macdidi.myandroidtutorial”套件下的“ItemActivity.java”,参考下列的说明加入启动元件的程式码:

public void clickFunction(View view) {
    int id = view.getId();

    switch (id) {
        case R.id.take_picture:
            break;
        case R.id.record_sound:
            break;
        case R.id.set_location:
            break;
        case R.id.set_alarm:
            break;
        case R.id.select_color:
            // 启动设定颜色的Activity元件
            startActivityForResult(
                    new Intent(this, ColorActivity.class), START_COLOR);
            break;
    }

}

同样在ItemActivity.java,参考下列的程式码,执行选择颜色后的设定工作:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode == Activity.RESULT_OK) {
        switch (requestCode) {
            case START_CAMERA:
                break;
            case START_RECORD:
                break;
            case START_LOCATION:
                break;
            case START_ALARM:
                break;
            // 设定颜色
            case START_COLOR:
                int colorId = data.getIntExtra(
                        "colorId", Colors.LIGHTGREY.parseColor());
                item.setColor(getColors(colorId));
                break;
        }
    }
}

private Colors getColors(int color) {
    Colors result = Colors.LIGHTGREY;

    if (color == Colors.BLUE.parseColor()) {
        result = Colors.BLUE;
    }
    else if (color == Colors.PURPLE.parseColor()) {
        result = Colors.PURPLE;
    }
    else if (color == Colors.GREEN.parseColor()) {
        result = Colors.GREEN;
    }
    else if (color == Colors.ORANGE.parseColor()) {
        result = Colors.ORANGE;
    }
    else if (color == Colors.RED.parseColor()) {
        result = Colors.RED;
    }

    return result;
}

执行应用程式,在新增或修改记事资料的时候,执行设定颜色的测试。

9-6 选择记事资料与主功能表

这一章最后的工作是完成让使用者勾选记事资料、控制主功能表的显示与删除记事的功能。开启在“net.macdidi.myandroidtutorial”套件下的“MainActivity.java”,找到“processControllers”方法,修改记事项目长按事件的程式码,原来的点击事件也要执行相关的修改。因为在使用者勾选事件项目以后,主功能表就要根据选择的情况调整,所以也增加控制功能表显示的方法processMenu:

private void processControllers() {

    // 建立选单项目点击监听物件
    AdapterView.OnItemClickListener itemListener = new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView parent, View view,
                                int position, long id) {
            // 读取选择的记事物件
            Item item = itemAdapter.getItem(position);

            // 如果已经有勾选的项目
            if (selectedCount > 0) {
                // 处理是否显示已选择项目
                processMenu(item);
                // 重新设定记事项目
                itemAdapter.set(position, item);
            }
            else {
                Intent intent = new Intent(
                        "net.macdidi.myandroidtutorial.EDIT_ITEM");

                // 设定记事编号与记事物件
                intent.putExtra("position", position);
                intent.putExtra("net.macdidi.myandroidtutorial.Item", item);

                startActivityForResult(intent, 1);
            }
        }
    };

    // 注册选单项目点击监听物件
    item_list.setOnItemClickListener(itemListener);

    // 建立记事项目长按监听物件
    AdapterView.OnItemLongClickListener itemLongListener = new AdapterView.OnItemLongClickListener() {
        @Override
        public boolean onItemLongClick(AdapterView parent, View view,
                                       int position, long id) {
            // 读取选择的记事物件
            Item item = itemAdapter.getItem(position);
            // 处理是否显示已选择项目
            processMenu(item);
            // 重新设定记事项目
            itemAdapter.set(position, item);
            return true;
        }
    };

    // 注册记事项目长按监听物件
    item_list.setOnItemLongClickListener(itemLongListener);

    ...
}

// 处理是否显示已选择项目
private void processMenu(Item item) {
    // 如果需要设定记事项目
    if (item != null) {
        // 设定已勾选的状态
        item.setSelected(!item.isSelected());

        // 计算已勾选数量
        if (item.isSelected()) {
            selectedCount++;
        }
        else {
            selectedCount--;
        }
    }

    // 根据选择的状况,设定是否显示选单项目
    add_item.setVisible(selectedCount == 0);
    search_item.setVisible(selectedCount == 0);
    revert_item.setVisible(selectedCount > 0);
    share_item.setVisible(selectedCount > 0);
    delete_item.setVisible(selectedCount > 0);
}

同样在“MainActivity.java”,找到“onCreateOptionsMenu”方法,为了控制主功能表的显示,参考下列的程式码执行必要的修改:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater menuInflater = getMenuInflater();
    menuInflater.inflate(R.menu.menu_main, menu);

    // 取得选单项目物件
    add_item = menu.findItem(R.id.add_item);
    search_item = menu.findItem(R.id.search_item);
    revert_item = menu.findItem(R.id.revert_item);
    share_item = menu.findItem(R.id.share_item);
    delete_item = menu.findItem(R.id.delete_item);

    // 设定选单项目
    processMenu(null);

    return true;
}

同样在“MainActivity.java”,找到“clickMenuItem”方法,加入取消勾选与删除记事资料的程式码:

public void clickMenuItem(MenuItem item) {
    // 使用参数取得使用者选择的选单项目元件编号
    int itemId = item.getItemId();

    // 判断该执行什么工作,目前还没有加入需要执行的工作
    switch (itemId) {
        case R.id.search_item:
            break;
        // 使用者选择新增选单项目
        case R.id.add_item:
            // 使用Action名称建立启动另一个Activity元件需要的Intent物件
            Intent intent = new Intent("net.macdidi.myandroidtutorial.ADD_ITEM");
            // 呼叫“startActivityForResult”,,第二个参数“0”表示执行新增
            startActivityForResult(intent, 0);
            break;
        // 取消所有已勾选的项目
        case R.id.revert_item:
            for (int i = 0; i < itemAdapter.getCount(); i++) {
                Item ri = itemAdapter.getItem(i);

                if (ri.isSelected()) {
                    ri.setSelected(false);
                    itemAdapter.set(i, ri);
                }
            }

            selectedCount = 0;
            processMenu(null);

            break;
        // 删除
        case R.id.delete_item:
            // 没有选择
            if (selectedCount == 0) {
                break;
            }

            // 建立与显示询问是否删除的对话框
            AlertDialog.Builder d = new AlertDialog.Builder(this);
            String message = getString(R.string.delete_item);
            d.setTitle(R.string.delete)
                    .setMessage(String.format(message, selectedCount));
            d.setPositiveButton(android.R.string.yes,
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            // 删除所有已勾选的项目
                            int index = itemAdapter.getCount() - 1;

                            while (index > -1) {
                                Item item = itemAdapter.get(index);

                                if (item.isSelected()) {
                                    itemAdapter.remove(item);
                                }

                                index--;
                            }

                            // 通知资料改变
                            itemAdapter.notifyDataSetChanged();
                            selectedCount = 0;
                            processMenu(null);                                
                        }
                    });
            d.setNegativeButton(android.R.string.no, null);
            d.show();

            break;
        case R.id.googleplus_item:
            break;
        case R.id.facebook_item:
            break;
    }

}

完成这一章所有的工作了,执行应用程式,看看加入的功能是不是都可以正常的运作。

文章导航