如何在由数据库游标适配器支持的Android ListView中的布局中隐藏一个视图

时间:2016-08-26 21:45:17

标签: android listview android-cursoradapter android-viewholder

示例应用程序的目标是显示SQLite数据库中的项目,但如果数据库记录激活了隐藏标记,则隐藏第二个文本视图(否则显示第二个文本视图)。

问题在于它并没有隐藏正确的东西。并且当滚动动作导致项目离开视图并返回视图时,第二个文本视图将被隐藏并以不稳定的方式显示在各种列表项上。

已在项目5,10,15,20上设置隐藏标记,以及它是如何出现的:initial page 6 unexpectedly hidden several unexpectedly hidden items more unexpectedly hidden images 向下滚动,隐藏了各种其他奇怪的项目,每次看起来都不一样。例如,条目14,条目16被隐藏。

滚动到顶部后,我们看到第一组项目不再具有相同的隐藏第二行。

after scrolling to the top, different things are hidden

然后隐藏一组全新的条目来回滚动。不是随意的,而是莫名其妙的。你必须要相信它。

此示例所基于的“真实”应用程序(此处未显示)实际上是在尝试显示和隐藏ImageView,但是同样的问题围绕着隐藏TextView,所以这就是我在这里展示的内容。

以下是申请表。 您需要的所有内容都应包含在内包括示例数据),如果您希望运行这个疯狂的事情。或者你可以在github上找到它:https://github.com/sengsational/LvCaApp

LvCaActivity.java

public class LvCaActivity extends AppCompatActivity {

    private SimpleCursorAdapter dataAdapter;
    private DbAdapter dbHelper;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lv_ca);

        dbHelper = new DbAdapter(this);
        dbHelper.open();
        dbHelper.deleteAll();
        dbHelper.insertSome();

        Cursor bCursor = dbHelper.fetchAll(DbAdapter.bColumns);

        dataAdapter = new MySimpleCursorAdapter(
                this, R.layout.b_item,
                bCursor,
                DbAdapter.bColumns,
                ViewHolder.viewsArray,
                0);

        ListView listView = (ListView) findViewById(R.id.listView1);

        listView.setAdapter(dataAdapter);

    }
}

activity_lv_ca.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent" android:layout_height="fill_parent"
              android:orientation="vertical">

    <ListView android:id="@+id/listView1" android:layout_width="fill_parent"
              android:layout_height="fill_parent" />

</LinearLayout>

b_item.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:padding="6dip"
                android:id="@+id/b_item_layout">

        <TextView
            android:id="@+id/bName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true"
            android:textAppearance="?android:attr/textAppearanceListItem"
            android:ellipsize="end"
            android:singleLine="true"
            android:paddingTop="30dp"/>

        <TextView
            android:id="@+id/bSecondLine"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/bName"
            android:textAppearance="?android:attr/textAppearanceSmall" />

        <TextView
            android:id="@+id/bDbItem"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:visibility="gone"
            />
        <TextView
            android:id="@+id/bHidden"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:visibility="gone"
            />

</RelativeLayout>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.company.cpp.lvcaapp"
          xmlns:android="http://schemas.android.com/apk/res/android">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".LvCaActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

DbAdapter.java

public class DbAdapter {

    private static final String TAG = "DbAdapter";
    private DatabaseHelper mDbHelper;
    private SQLiteDatabase mDb;

    private static final String DATABASE_NAME = "adbname";
    private static final String SQLITE_TABLE = "atablename";
    private static final int DATABASE_VERSION = 1;

    private final Context mCtx;

    public static final String[] bColumns = new String[] {
            "_id",
            "NAME",
            "SECOND_LINE",
            "HIDDEN",
    };

    private static final String DATABASE_CREATE =
            "CREATE TABLE if not exists " + SQLITE_TABLE + " (" +
                    "_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
                    "NAME TEXT, " +
                    "SECOND_LINE, " +
                    "HIDDEN" +
                    ");";

    private static class DatabaseHelper extends SQLiteOpenHelper {
        DatabaseHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            Log.w(TAG, DATABASE_CREATE);
            db.execSQL(DATABASE_CREATE);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
                    + newVersion + ", which will destroy all old data");
            db.execSQL("DROP TABLE IF EXISTS " + SQLITE_TABLE);
            onCreate(db);
        }
    }

    public DbAdapter(Context ctx) {
        this.mCtx = ctx;
    }

    public DbAdapter open() throws SQLException {
        mDbHelper = new DatabaseHelper(mCtx);
        mDb = mDbHelper.getWritableDatabase();
        return this;
    }

    public void close() {
        if (mDbHelper != null) {
            mDbHelper.close();
        }
    }
    public Cursor fetchAll(String[] fields) {
        Cursor mCursor = mDb.query(SQLITE_TABLE, fields, null, null, null, null, null);
        if (mCursor != null) {
            mCursor.moveToFirst();
        }
        return mCursor;
    }

    public void insertSome() {
        AListItem.getInstance();
        String sampleData = "[{\"name\":\"Entry 1\",\"second_line\":\"Second Line 1\",\"hidden\":\"F\"},{\"name\":\"Entry 2\",\"second_line\":\"Second Line 2\",\"hidden\":\"F\"},{\"name\":\"Entry 3\",\"second_line\":\"Second Line 3\",\"hidden\":\"F\"},{\"name\":\"Entry 4\",\"second_line\":\"Second Line 4\",\"hidden\":\"F\"},{\"name\":\"EntryH 5\",\"second_line\":\"Second Line 5\",\"hidden\":\"T\"},{\"name\":\"Entry 6\",\"second_line\":\"Second Line 6\",\"hidden\":\"F\"},{\"name\":\"Entry 7\",\"second_line\":\"Second Line 7\",\"hidden\":\"F\"},{\"name\":\"Entry 8\",\"second_line\":\"Second Line 8\",\"hidden\":\"F\"},{\"name\":\"Entry 9\",\"second_line\":\"Second Line 9\",\"hidden\":\"F\"},{\"name\":\"EntryH 10\",\"second_line\":\"Second Line 10\",\"hidden\":\"T\"},{\"name\":\"Entry 11\",\"second_line\":\"Second Line 11\",\"hidden\":\"F\"},{\"name\":\"Entry 12\",\"second_line\":\"Second Line 12\",\"hidden\":\"F\"},{\"name\":\"Entry 13\",\"second_line\":\"Second Line 13\",\"hidden\":\"F\"},{\"name\":\"Entry 14\",\"second_line\":\"Second Line 14\",\"hidden\":\"F\"},{\"name\":\"EntryH 15\",\"second_line\":\"Second Line 15\",\"hidden\":\"T\"},{\"name\":\"Entry 16\",\"second_line\":\"Second Line 16\",\"hidden\":\"F\"},{\"name\":\"Entry 17\",\"second_line\":\"Second Line 17\",\"hidden\":\"F\"},{\"name\":\"Entry 18\",\"second_line\":\"Second Line 18\",\"hidden\":\"F\"},{\"name\":\"Entry 19\",\"second_line\":\"Second Line 19\",\"hidden\":\"F\"},{\"name\":\"EntryH 20\",\"second_line\":\"Second Line 20\",\"hidden\":\"T\"},{\"name\":\"Entry 21\",\"second_line\":\"Second Line 21\",\"hidden\":\"F\"},{\"name\":\"Entry 22\",\"second_line\":\"Second Line 22\",\"hidden\":\"F\"},{\"name\":\"Entry 23\",\"second_line\":\"Second Line 23\",\"hidden\":\"F\"}]";
        String[] items = sampleData.split("\\},\\{");
        for(String item: items){
            AListItem.clear();
            AListItem.load(item);
            if(AListItem.getName().contains("Hide")){
                AListItem.setHidden("T");
            }

            mDb.insert(SQLITE_TABLE, null, AListItem.getContentValues());

            ContentValues values = AListItem.getContentValues();
            Log.v(TAG, "values.toString()" + values.toString());
        }
    }

    public boolean deleteAll() {
        int doneDelete = 0;
        doneDelete = mDb.delete(SQLITE_TABLE, null , null);
        Log.w(TAG, Integer.toString(doneDelete));
        return doneDelete > 0;
    }
}

AListItem.java

public class AListItem {

    static String rawInputString;

    static String name;
    static String second_line;
    static String hidden;

    static AListItem aListItem;

    private AListItem() {
    }

    public static AListItem getInstance(){
        if (aListItem == null) {
            aListItem = new AListItem();
        }
        return aListItem;
    }

    public static void clear() {
        rawInputString = null;
        name = null;
        second_line = null;
        hidden = null;
    }

    public static ContentValues getContentValues() {
        ContentValues values = new ContentValues();
        values.put("NAME", name);
        values.put("SECOND_LINE", second_line);
        values.put("HIDDEN",  hidden);
        return values;
    }


    public static void load(String string) {
        StringBuffer buf = new StringBuffer(string);
        if (buf.substring(0,2).equals("[{")){
            buf.delete(0,2);
        }
        rawInputString = buf.toString();
        parse();
    }

    public static void parse() {

        if (rawInputString == null) {
            System.out.println("nothing to parse");
            return;
        }

        rawInputString = rawInputString.replaceAll("\"\\:null,", "\"\\:\"null\",");
        String[] nvpa = rawInputString.split("\",\"");
        for (String nvpString : nvpa) {
            String[] nvpItem = nvpString.split("\":\"");
            if (nvpItem.length < 2) continue;
            String identifier = nvpItem[0].replaceAll("\"", "");
            String content = nvpItem[1].replaceAll("\"", "");

            switch (identifier) {
                case "name":
                    setName(content);
                    break;
                case "second_line":
                    setSecond_line(content);
                    break;
                case "hidden":
                    setHidden(content);
                    break;
                default:
                    System.out.println("nowhere to put [" + nvpItem[0] + "] " + nvpString + " raw: " + rawInputString);
                    break;
            }
        }
    }

    public static String getName() {
        return name;
    }

    public static void setName(String name) {  AListItem.name = name; }

    public static void setSecond_line(String second_line) {
        AListItem.second_line = second_line;
    }

    public static String getSecond_line() {
        return second_line;
    }

    public static void setHidden(String hidden) {
        AListItem.hidden = hidden;
    }

    public static String getHidden() {
        return hidden;
    }

    public String toString() {
        return getName() + ", " +
                getSecond_line() + ", " +
                getHidden();
    }

}

MySimpleCursorAdapter.java

public class MySimpleCursorAdapter extends SimpleCursorAdapter {

    Context context;
    Cursor cursor;
    public static final String TAG = "MySimpleCursorAdapter";

    public MySimpleCursorAdapter(Context context, int layout, Cursor cursor, String[] from, int[] to, int flags) {
        super(context, layout, cursor, from, to, flags);
        this.context = context;
        this.cursor = cursor;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Log.v(TAG,"getView() >>>>>>STARTING");

        ViewHolder viewHolder;
        LayoutInflater inflater = LayoutInflater.from(context);
        if (null == convertView || null == convertView.getTag()) {
            convertView = inflater.inflate(R.layout.b_item, null);
            viewHolder = new ViewHolder(convertView);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        for (int i = 0; i < cursor.getColumnCount(); i++) {
            Log.v(TAG, "getView cursor " + i + ": " + cursor.getString(i));
        }

        String hidden = cursor.getString(ViewHolder.HIDDEN);
        if (hidden == null) hidden = "F";
        Log.v(TAG,"Hidden State: " + hidden);
        switch (hidden) {
            case "F":
               viewHolder.showSecondLine(); // DRS 20160827 - Added line suggested by aiwiguna
                break;
            case "T":
                Log.v(TAG,">>>>>Hidden was TRUE<<<<<<<: " + cursor.getString(ViewHolder.NAME));
                viewHolder.hideSecondLine();
                break;
        }

        convertView.setTag(viewHolder);
        View returnView = super.getView(position, convertView, parent);

        Log.v(TAG,"getView() ENDING<<<<<<<<<");

        return returnView;
    }
}

ViewHolder.java

class ViewHolder {

    public static final String TAG = "ViewHolder";

    public static final int DB_ITEM = 0;
    public static final int NAME = 1;
    public static final int SECOND_LINE = 2;
    public static final int HIDDEN = 3;

    public static final int[] viewsArray = new int[] {
            R.id.bDbItem,
            R.id.bName,
            R.id.bSecondLine,
            R.id.bHidden,
    };

    public static final TextView[] textViewArray = new TextView[viewsArray.length];

    public ViewHolder( final View root ) {
        Log.v(TAG, "ViewHolder constructor");
        for (int i = 0; i < viewsArray.length; i++) {
            textViewArray[i] = (TextView) root.findViewById(viewsArray[i]);
            Log.v(TAG, "             textViewArray[" + i + "]: " + textViewArray[i]);
        }
    }

    public void hideSecondLine() {
        textViewArray[SECOND_LINE].setVisibility(View.INVISIBLE);
    }

    //DRS 20160827 - Addition recommended by aiwiguna
    public void showSecondLine() {
        textViewArray[SECOND_LINE].setVisibility(View.VISIBLE);
    }

}

3 个答案:

答案 0 :(得分:1)

为了让这个应用程序正常工作,

  1. ListView必须替换为RecyclerView,加上
  2. SimpleCursorAdapter实施需要由RecyclerCursorAdapter实施替代。
  3. MyRecyclerCursorAdapter,示例中的新类,扩展了RecyclerView.Adapter

    //DRS 20160829 - Added class.  Replaces MySimpleCursorAdapter
    public class MyRecyclerCursorAdapter extends RecyclerView.Adapter{
    
        private Cursor cursor;
        private Context context;
        private static final String TAG = MyRecyclerCursorAdapter.class.getSimpleName();
    
        public MyRecyclerCursorAdapter(Context context, Cursor cursor) {
            this.cursor = cursor;
            this.context = context;
        }
    
        //DRS 20160829 - Critical method within new class
        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            Log.v(TAG, "onCreateViewHolder ");
            Context context = parent.getContext();
            LayoutInflater inflater = LayoutInflater.from(context);
    
            View itemView = inflater.inflate(R.layout.b_item, parent, false);
    
            ViewHolder viewHolder = new ViewHolder(itemView, cursor);
            return viewHolder;
        }
    
        //DRS 20160829 - Critical method within new class
        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            cursor.moveToPosition(position);
            ((ViewHolder)holder).bindFields(cursor);
        }
    
        @Override
        public int getItemCount() {
            return cursor.getCount();
        }
    }
    

    请注意,此类带有一个Cursor对象,该对象是将填充列表的SQLite数据库条目的链接。

    此外,为了获得对Recycler View的访问权限,必须在build.gradle添加依赖关系:

    // DRS 20160829 - Added recyclerview
    dependencies {
        compile fileTree(include: ['*.jar'], dir: 'libs')
        testCompile 'junit:junit:4.12'
        compile 'com.android.support:appcompat-v7:24.2.0'
        compile 'com.android.support:recyclerview-v7:24.2.0'
    }
    

    b_item.xml不需要更改 activity_lv_ca.xml需要RecyclerView代替旧ListView

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent" android:layout_height="fill_parent"
              android:orientation="vertical">
    
        <!-- DRS 20160829 - Commented ListView, Added RecyclerView
        <ListView android:id="@+id/listView1" android:layout_width="fill_parent"
              android:layout_height="fill_parent" / -->
        <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical" />
    
    </LinearLayout>
    

    ViewHolder课程现在扩展Recycler.ViewHolder。除了标准ViewHolder实施之外,此自定义ViewHolder还有一个Cursor,用于设置列表每行上显示的TextViews的文本。这就是管理可见性的地方(在我称之为bindFields()的方法中:

    public class ViewHolder extends RecyclerView.ViewHolder {
        public static final String TAG = "ViewHolder";
        private final Cursor cursor;
    
        public TextView bDbItem;
        public TextView bName;
        public TextView bSecondLine;
        public TextView bHidden;
    
        public static final int DB_ITEM = 0;
        public static final int NAME = 1;
        public static final int SECOND_LINE = 2;
        public static final int HIDDEN = 3;
    
        public ViewHolder(View root, Cursor cursor ) {
            super(root);
            this.cursor = cursor;
            Log.v(TAG, "ViewHolder constructor");
            bDbItem = (TextView) itemView.findViewById(R.id.bDbItem);
            bName = (TextView) itemView.findViewById(R.id.bName);
            bSecondLine = (TextView) itemView.findViewById(R.id.bSecondLine);
            bHidden = (TextView) itemView.findViewById(R.id.bHidden);
        }
    
        public void bindFields(Cursor cursor) {
    
            bDbItem.setText("" + cursor.getInt(DB_ITEM));
            bName.setText(cursor.getString(NAME));
            bSecondLine.setText(cursor.getString(SECOND_LINE));
    
            String hidden = cursor.getString(HIDDEN);
            bHidden.setText(hidden);
            if ("F".equals(hidden)) {
                bSecondLine.setVisibility(View.VISIBLE);
            } else {
                bSecondLine.setVisibility(View.INVISIBLE);
            }
        }
    }
    

    AListItem.java不需要更改 DBAdapter.java不需要更改。

    可以在github上找到工作申请:RecyclerViewSqlite

答案 1 :(得分:0)

在您当前的解决方案中,您需要在循环视图中“取消隐藏”第二行。最简单的方法是在switch语句中。 FWIW,您可以将SQLite表中的“隐藏”标志存储为整数值。这将使比较更容易,并且可能稍快一些。

另一种可能的解决方案是为正常情况设置两种不同的布局(hidden为假),一种用于“隐藏”情况(hidden为真)。在MySimpleCursorAdapter.getView()中,if语句决定要扩张的布局。在回收视图时,您仍会遇到相同的问题:在重新使用视图之前,请检查以确保循环视图的类型正确。

答案 2 :(得分:0)

switch (hidden) {
            case "F":
                viewHolder.showSecondLine();
                break;
            case "T":
                Log.v(TAG,">>>>>Hidden was TRUE<<<<<<<: " + cursor.getString(ViewHolder.NAME));
                viewHolder.hideSecondLine();
                break;
        }