示例应用程序的目标是显示SQLite数据库中的项目,但如果数据库记录激活了隐藏标记,则隐藏第二个文本视图(否则显示第二个文本视图)。
问题在于它并没有隐藏正确的东西。并且当滚动动作导致项目离开视图并返回视图时,第二个文本视图将被隐藏并以不稳定的方式显示在各种列表项上。
已在项目5,10,15,20上设置隐藏标记,以及它是如何出现的: 向下滚动,隐藏了各种其他奇怪的项目,每次看起来都不一样。例如,条目14,条目16被隐藏。
滚动到顶部后,我们看到第一组项目不再具有相同的隐藏第二行。
然后隐藏一组全新的条目来回滚动。不是随意的,而是莫名其妙的。你必须要相信它。
此示例所基于的“真实”应用程序(此处未显示)实际上是在尝试显示和隐藏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);
}
}
答案 0 :(得分:1)
为了让这个应用程序正常工作,
ListView
必须替换为RecyclerView
,加上SimpleCursorAdapter
实施需要由RecyclerCursorAdapter
实施替代。 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;
}