Android 使用 SQLite 光标显示下一项或上一项

Posted

技术标签:

【中文标题】Android 使用 SQLite 光标显示下一项或上一项【英文标题】:Android using SQLite cursor to display next item or previous item 【发布时间】:2016-05-16 19:22:05 【问题描述】:

一个常见的 android 应用程序功能是在每个详细项目上滑动以获取下一个项目或上一个项目。

数据库游标包含在 ListActivity 的 onCreate 中显示的项目列表:

if (cursor.moveToFirst()) 
    listThings.setAdapter(new ResourceCursorAdapter(this, R.layout.my_simple_expandable_list_item_2, cursor) 
        @Override
        public void bindView(View view, Context context, Cursor cursor) 
            TextView tvFirst = (TextView)view.findViewById(android.R.id.text1);
            TextView tvSecond = (TextView)view.findViewById(android.R.id.text2);

            tvFirst.setText(cursor.getString(1) + "  -  " + getPctString(cursor.getString(2)));
            tvSecond.setText(cursor.getString(3));
        
    );
 else 
....

效果很好。内置布局资源R.layout.my_simple_expandable_list_item_2 告诉适配器在单个文本视图中显示光标中的每个项目。点击后,我可以在后续活动中显示该项目的详细信息:

AdapterView.OnItemClickListener itemClickListener = new AdapterView.OnItemClickListener() 
    public void onItemClick(AdapterView<?> listView, View v, int position, long id) 
    Intent intent = new Intent(ThingListActivity.this, ThingActivity.class);
    intent.putExtra(FollowOnActivity.EXTRA_ID, (int) id);
    startActivity(intent);
    
;

FollowOnActivity 中,我可以查找详细信息并填充视图。我还可以收听滑动并做出反应。

问题:我无法访问列表中的下一个和上一个项目。到目前为止,我还没有提出我认为的干净的解决方案。由于这是一个常见的要求,因此可能有一个很好的模式。我想知道那是什么。

我能够让这个screen slide example 工作,但该示例中的列表固定为五个项目,并且这些项目是 xml 文件中的文本而不是数据库项目。我基于他们的ScreenSlidePagerActivity 创建了ThingSlideActivity,基于他们的ScreenSlidePageAdapterThingSlideFragment 基于他们的ScreenSlidePageFragment 创建了ThingSlidePageAdapter。但我不知道如何将其连接到我来自的列表视图。它可能有一个很大的列表,而我尽量不提供所有这些的详细信息

我正坐在一个已填充光标的列表视图中(此问题顶部的第一个代码块)。用户单击列表中的随机一个,onListItemClick 运行。应该怎么做才能左右滑动查看上一个和下一个项目?

【问题讨论】:

看看接受的答案here。 这个答案相当简陋;我不明白。它说“使用适配器的getItem(int position)方法,它返回给你指定位置的列表项”,但是在FollowOnActivity的OnTouchListener中,我看不到如何获取早期ListView的适配器。 【参考方案1】:

您运行了一个查询,并为您的ListActivity 获得了一个Cursor。您将 Cursor 附加到了正在显示您的列表的 CursorAdapter

如果用户选择第三个项目(项目 2),那么在您的 OnItemClickListener 中,position 参数将为 2。

那么,如果您将位置 (2) 传递给后续活动,在那里运行相同的查询并从光标第 2 行开始呢?使用光标,您会知道上一个、下一个……您将拥有列表中所有内容的数据!

显示大型细节视图并水平左右滑动它们的方法是使用一个名为ViewPager 的小部件。 ViewPager 是由 PagerAdapter 驱动的,它有一些相似之处,但绝对不同于 ListAdapter

您要做的是创建一个具有您的CursorPagerAdapter,因此当您创建一个页面时,您可以将来自Cursor 的数据提供给它。当适配器被要求提供第 3 页时,您创建一个详细视图,从光标读取第三条记录并将该数据放入详细视图中。

真的就这么简单。 ResourceCursorAdapter 在幕后做这件事。当适配器被要求提供第三项的视图时,会创建/回收一个视图,将光标定位在第三条记录上,最后调用您的 bindView 方法将数据放入视图中。

这是PagerAdapter 的粗略示例:

public class CursorPagerAdapter extends PagerAdapter 

    /** Note that this class does not handle closing the cursor. */
    private Cursor mCursor;

    public CursorPagerAdapter(Cursor mCursor) 
        this.mCursor = mCursor;
    

    @Override
    public int getCount() 
        return mCursor == null ? 0 : mCursor.getCount();
    

    @Override
    public Object instantiateItem(ViewGroup container, int position) 

        mCursor.moveToPosition(position);
        String data1 = mCursor.getString(1);
        String data2 = mCursor.getString(2);
        String data3 = mCursor.getString(3);

        View view = LayoutInflater.from(container.getContext()).inflate(R.layout.detail_view, container, false);

        // TODO fill in the view with the cursor data

        container.addView(view);
        return view;
    

    @Override
    public boolean isViewFromObject(View view, Object object) 
        return view == object;
    

ViewPager 是一个狡猾的小家伙,但作为一名 Android 开发人员,你迟早会需要它,所以现在就开始学习如何使用它们是件好事。我建议您深入研究并查看任何您可以获得的 ViewPager 教程。

【讨论】:

ViewPager 可能是我正在寻找的“好模式”! 很多滑动视图教程都在更改选项卡,这当然不是我想要的。搜索“viewpager cursor swipe”,我找到了***.com/questions/22162589/…,我现在正在解决这个问题。【参考方案2】:

这就是最终的工作。它使用来自 developer.android.com/training 的screen-slide example 中的模式,但有一个区别:在ThingSlideActivity.getItem() 中,我使用了ThingSlideFragment.onCreate(pos),而不是运行ThingSlideFragment.onCreate(pos)。此外,我添加了static Cursor getCursor(),它提供了最初填充ListActivity 的原始Cursor。这允许我从列表中的位置转换为数据库键。如果这个模式有什么问题,请指教。

初始活动此活动有一个来自数据库的列表和一个光标保持打开状态直到列表被销毁

public class ThingListActivity extends ListActivity 
    private SQLiteDatabase db;
    private static Cursor cursor;

    static Cursor getCursor() 
        return cursor;
    

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        ListView listThings = getListView();
        int position = getIntent().getIntExtra("POSITION", 0);

        try 
            SQLiteOpenHelper tableDatabaseHelper = new TableDatabaseHelper(this);
            db = tableDatabaseHelper.getReadableDatabase();
            cursor = db.query("TABLE", pullFields, selectionFields, selectionArgs, null, null, null); // you define your own query

            if (cursor.moveToFirst()) 
                listThings.setAdapter(new ResourceCursorAdapter(this, R.layout.my_simple_expandable_list_item_2, cursor)  //built-in layout
                    @Override
                    public void bindView(View view, Context context, Cursor cursor) 
                        TextView tvFirst = (TextView)view.findViewById(android.R.id.text1);
                        TextView tvSecond = (TextView)view.findViewById(android.R.id.text2);
                        tvFirst.setText(cursor.getString(1) + "  -  " + getPctString(cursor.getString(2)));
                        tvSecond.setText(cursor.getString(3));
                    
                );
             else 
                Toast toast = Toast.makeText(this, "The list was empty.", Toast.LENGTH_SHORT);
                toast.show();
            
         catch (SQLiteException e) 
            Toast toast = Toast.makeText(this, "Database Unavailable", Toast.LENGTH_SHORT);
            toast.show();
         finally 
            //Not closing cursor.  Will do in onDestroy()
        
    

    @Override
    public void onDestroy()
        super.onDestroy();
        if (cursor != null) cursor.close();
        if (db != null) db.close();
    

    @Override
    protected void onListItemClick(ListView listView, View view, int position, long id) 
        Intent intent = new Intent(ThingListActivity.this, ThingSlideActivity.class);
        intent.putExtra(ThingSlideActivity.EXTRA_POSITION, (int) position);
        startActivity(intent);
    

后续FragmentActivity此活动有一个内部类ThingSlidePageAdapter。注意setCurrentItem,它将您放在列表中的正确位置。

public class ThingSlideActivity extends FragmentActivity

    public static final String EXTRA_POSITION = "extra_position";

    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_screen_slide);
        ViewPager mPager = (ViewPager)findViewById(R.id.pager);
        PagerAdapter mPagerAdapter = new ThingSlidePageAdapter(getSupportFragmentManager());
        mPager.setAdapter(mPagerAdapter);
        mPager.setCurrentItem((Integer)getIntent().getExtras().get(EXTRA_POSITION));
    

    private static class ThingSlidePageAdapter extends FragmentStatePagerAdapter 

        int count;

        public ThingSlidePageAdapter(FragmentManager fm)
            super(fm);
            this.count = ThingListActivity.getCursor().getCount();
        

        @Override
        public int getCount() 
            return this.count;
        

        @Override
        public Fragment getItem(int position) 
            return ThingSlideFragment.newInstance(position); // onCreate() doesn't work well.
        
    

**我们正在翻页的Fragment。这是我们使用原始ListView 中的Cursor 来获取数据库密钥(_id)的地方。然后执行一个新的查询,只提取一个项目的详细信息。但在幕后,上一个和下一个项目也被拉出来了。

public class ThingSlideFragment extends Fragment 

    private static final String ARG_POSITION = "position";

    public static ThingSlideFragment create(int position) 
        ThingSlideFragment fragment = new ThingSlideFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_POSITION, position);
        fragment.setArguments(args);
        return fragment;
    

    public static ThingSlideFragment newInstance(int position) 
        ThingSlideFragment fragment = new ThingSlideFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_POSITION, position);
        fragment.setArguments(args);
        return fragment;
    

    public ThingSlideFragment() 
    

    @Override
    public void onCreate(Bundle savedInstanceState)
        super.onCreate(savedInstanceState);
    

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
        ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.activity_thing, container, false);
        TextView tv = (TextView)rootView.findViewById(R.id.thingname);
        Cursor mCursor = ThingListActivity.getCursor();
        if (mCursor != null) 
            mCursor.moveToPosition(getArguments().getInt(ARG_POSITION));
            int id = Integer.parseInt(mCursor.getString(0));
            populateView(id, rootView);
        
        return rootView;
    

    private void populateView(int id, View rootView) 
        Cursor cursor = null;
        SQLiteDatabase db = null;
        try 
            SQLiteOpenHelper tableDatabaseHelper = new TableDatabaseHelper(this.getContext()) ;
            db = tableDatabaseHelper.getReadableDatabase();
            cursor = db.query("TABLE", new String[] "NAME", "DESCRIPTION", "DETAIL1", "DETAIL2", "DETAIL3", "DETAIL4", "_id = ?", new String[] Integer.toString(id), null, null, null);

            //Move to first record in the cursor (should be just one since our query used database key "CREATE TABLE TABLE (_id INTEGER PRIMARY KEY AUTOINCREMENT, ...."
            if (cursor.moveToFirst()) 
                // Get thing details from the cursor
                String nameText = cursor.getString(0);
                String descriptionText = cursor.getString(1);
                // ........ continue with other fields  

                // Populate TextView items
                ((TextView)rootView.findViewById(R.id.thingname)).setText(nameText);
                ((TextView)rootView.findViewById(R.id.description)).setText(descriptionText);
                // ........ continue with other fields
            
         catch (SQLiteException e) 
            Toast toast = Toast.makeText(this.getContext(), "Database unavailable. "+ e.getMessage(), Toast.LENGTH_LONG)   ;
         finally 
            try cursor.close(); catch (Throwable t) 
            try db.close(); catch (Throwable t) 
        
    

而且,这里的许多示例都省略了支持 xml 文件的内容!

<!-- R.layout.activity_screen_slide.xml containing R.id.pager -->

<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/pager"
   android:layout_
   android:layout_ />

<!-- R.layout.activity_thing.xml containing R.id.thingname and R.id.description -->

<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_>

    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_
        android:layout_
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:id="@+id/thingRelativeLayout"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:context="com.someones.package.ThingActivity"
        tools:showIn="@layout/activity_thing">

        <TextView
            android:id="@+id/thingname"
            android:layout_
            android:layout_
            android:layout_alignParentTop="true"
            android:layout_alignParentLeft="true"
            android:paddingTop="40dp"
            android:textSize="40dp"
            android:gravity="center"
            tools:text="Name Of Thing"
            />
        <TextView
            android:id="@+id/description"
            android:layout_
            android:layout_
            android:layout_alignParentLeft="true"
            android:textSize="20dp"
            android:layout_below="@id/thingname"
            android:paddingTop="10dp"
            tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris et magna ut erat elementum cursus in quis ipsum. Nam faucibus ultrices eros, vel tempor leo semper sit amet."
            />
    </RelativeLayout>
</ScrollView>

我希望这对您有用,并希望很多人能够找到它并从中受益,因为我花了几天时间摆弄它,直到我满意为止。对于这里经验丰富的人来说可能一个简单的,但对具有一定经验的人有帮助。

【讨论】:

以上是关于Android 使用 SQLite 光标显示下一项或上一项的主要内容,如果未能解决你的问题,请参考以下文章

Android - 关闭数据库后可以使用 SQLite 光标吗?

Android SQLite - 光标和内容值

无法读取光标窗口android的行

如何使用光标和循环显示来自 sqlite 的片段的 recyclerview

SQLite Android 数据库光标窗口分配 2048 kb 失败

Android SQLite 删除问题