问题
I have MyDBHandler
with a getAllDetails
method that queries the database using a cursor and returns a list.
What I don't know what to do now is how to output this list in a listview
in another fragment. I was told to create two XML layouts and a custom adapter but I don't know how to this exactly!
MyDBHandler class
package com.astuetz.viewpager.extensions.sample;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.Cursor;
import android.content.Context;
import android.content.ContentValues;
import java.util.ArrayList;
import java.util.List;
public class MyDBHandler extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_NAME = "detailsDB.db";
public static final String TABLE_DETAILS = "details";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_FIRSTNAME = "firstname";
public static final String COLUMN_SURNAME = "surname";
public static final String COLUMN_PHONE = "phone";
public static final String COLUMN_EMAIL = "email";
public static final String COLUMN_ADDRESS1 = "address1";
public static final String COLUMN_ADDRESS2 = "address2";
public static final String TABLE_KIN_DETAILS = "kindetails";
public static final String COLUMN_KIN_ID = "_id";
public static final String COLUMN_KIN_YOUREMAIL = "youremailkin";
public static final String COLUMN_KIN_FIRSTNAME = "firstnamekin";
public static final String COLUMN_KIN_SURNAME = "surnamekin";
public static final String COLUMN_KIN_PHONE = "phonekin";
public static final String COLUMN_KIN_EMAIL = "emailkin";
public static final String COLUMN_KIN_ADDRESS1 = "address1kin";
public static final String COLUMN_KIN_ADDRESS2 = "address2kin";
// Pass database information along to superclass
public MyDBHandler(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, DATABASE_NAME, factory, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
String query = " CREATE TABLE " + TABLE_DETAILS + "("
+ COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ COLUMN_FIRSTNAME + " TEXT, "
+ COLUMN_SURNAME + " TEXT, "
+ COLUMN_PHONE + " TEXT, "
+ COLUMN_EMAIL + " TEXT, "
+ COLUMN_ADDRESS1 + " TEXT, "
+ COLUMN_ADDRESS2 + " TEXT "
+ ");";
String query2 = " CREATE TABLE " + TABLE_KIN_DETAILS + "("
+ COLUMN_KIN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ COLUMN_KIN_YOUREMAIL + " TEXT, "
+ COLUMN_KIN_FIRSTNAME + " TEXT, "
+ COLUMN_KIN_SURNAME + " TEXT, "
+ COLUMN_KIN_PHONE + " TEXT, "
+ COLUMN_KIN_EMAIL + " TEXT, "
+ COLUMN_KIN_ADDRESS1 + " TEXT, "
+ COLUMN_KIN_ADDRESS2 + " TEXT "
+ ");";
db.execSQL(query);
db.execSQL(query2);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(" DROP TABLE IF EXISTS " + TABLE_DETAILS);
db.execSQL(" DROP TABLE IF EXISTS " + TABLE_KIN_DETAILS);
onCreate(db);
}
//Add a new row to the database
public void addDetails(Details details) {
ContentValues values = new ContentValues();
values.put(COLUMN_FIRSTNAME, details.getFirstname());
values.put(COLUMN_SURNAME, details.getSurname());
values.put(COLUMN_PHONE, details.getPhone());
values.put(COLUMN_EMAIL, details.getEmail());
values.put(COLUMN_ADDRESS1, details.getAddress1());
values.put(COLUMN_ADDRESS2, details.getAddress2());
SQLiteDatabase db = getWritableDatabase();
db.insert(TABLE_DETAILS, null, values);
db.close();
}
public void addKinDetails(KinDetails kinDetails){
ContentValues values = new ContentValues();
values.put(COLUMN_KIN_YOUREMAIL, kinDetails.getyourEmailkin());
values.put(COLUMN_KIN_FIRSTNAME, kinDetails.getFirstnamekin());
values.put(COLUMN_KIN_SURNAME, kinDetails.getSurnamekin());
values.put(COLUMN_KIN_PHONE, kinDetails.getPhonekin());
values.put(COLUMN_KIN_EMAIL, kinDetails.getEmailkin());
values.put(COLUMN_KIN_ADDRESS1, kinDetails.getAddress1kin());
values.put(COLUMN_KIN_ADDRESS2, kinDetails.getAddress2kin());
SQLiteDatabase db = getWritableDatabase();
db.insert(TABLE_KIN_DETAILS, null, values);
db.close();
}
public List<Details> getAllDetails(){
//create a new list in which we put all persons
List<Details>detailsList = new ArrayList<>();
SQLiteDatabase db = getWritableDatabase();
String query = "SELECT * FROM " + TABLE_DETAILS;
//Cursor points to a location in your results
Cursor c = db.rawQuery(query, null);
//Move to the first row in your results
if (c != null) {
c.moveToFirst();
//Position after the last row means the end of the results
while (!c.isAfterLast()) {
//create new details object
Details details = new Details();
//Here use static declared on top of the class..don't use "" for the table column
details.set_id(c.getColumnIndex(COLUMN_ID));
details.setFirstname(c.getString(c.getColumnIndex(COLUMN_FIRSTNAME)));
details.setSurname(c.getString(c.getColumnIndex(COLUMN_SURNAME)));
details.setPhone(c.getString(c.getColumnIndex(COLUMN_PHONE)));
details.setEmail(c.getString(c.getColumnIndex(COLUMN_EMAIL)));
details.setAddress1(c.getString(c.getColumnIndex(COLUMN_ADDRESS1)));
details.setAddress2(c.getString(c.getColumnIndex(COLUMN_ADDRESS2)));
detailsList.add(details);
c.moveToNext();
}
c.close();
}
db.close();
//return our list of persons
return detailsList;
}
}
回答1:
You need to first create layout files for your fragment and the listview rows.
For the fragment you can create a new blank fragment as follows : Note: We are using a blank fragment because its good practise to learn since it gives you more control in complex situations.
In the fragment_details.xml paste the following code : Note change com.companyname to your app package name!
<LinearLayout 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"
tools:context="com.companyname.myapplication.FragmentDetails">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/frag_details_listView"
android:layout_weight="1" />
</LinearLayout>
Create a new xml layout file and name it row_details , this will be our custom row for our listview.
And in the file row_details.xml paste the following code :
<?xml version="1.0" encoding="utf-8"?>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Large Text"
android:id="@+id/row_details_textview_name"
android:layout_marginTop="10dp"
android:layout_marginLeft="10dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Small Text"
android:id="@+id/row_details_textview_id"
android:layout_marginLeft="10dp" />
</LinearLayout>
What we need now is a custom adapter which will take care of loading the data into our listview.
Create a new Java Class and name it DetailsAdapter, and paste the following code : Note see my comments in the code because it is very important to understand the concept behind the adapters in Android :
public class DetailsAdapter extends ArrayAdapter<Person> {
private Context context;
//Constructor
public DetailsAdapter(Context context, int resource, List<Person> objects) {
super(context, resource, objects);
this.context = context;
}
//The get view is the most crucial part of the adapter, here the listview asks the
//adapter for the row to display
@Override
public View getView(int position, View row, ViewGroup parent) {
//Get an instance of our holder
Holder holder;
//Check if this is the first time we are creating this row for the listview
if (row == null){
//Row was null and thus we need to get components from the row_details.xml
holder = new Holder();
//get the Android's layout inflater service which will read our row_details.xml
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//Fill our row view with the xml layout file
row = inflater.inflate(R.layout.row_details, null);
//Fill our holder with the text view components
holder.textViewName = (TextView)row.findViewById(R.id.row_details_textview_name);
holder.textViewId = (TextView)row.findViewById(R.id.row_details_textview_id);
//This is very imp! attach our holder to the row
row.setTag(holder);
}else{
//row was created before! thus get the holder object from the row tag
holder = (Holder)row.getTag();
}
//At this point we have our row, either created from new or got it from the row tag object
//we can now fill the data
//First get our object from the list which is in the position of the listview
//The position as you can see is passed to the getView method by the listview
Person person = getItem(position);
holder.textViewName.setText(person.getFirstname());
holder.textViewId.setText("ID: " + person.get_id());
//we are done formatting our row..return to listview to show
return row;
}
//A holder will be resposable to hold our components to improve listview performance
//We replicate the components we have in the row_details.xml
private class Holder{
TextView textViewName;
TextView textViewId;
}
}
At this point we are ready to rumble!!
In the FragmentDetails java class we declare a global private List of Details...we get an instance of our listview..we get the data..create a new DetailsAdapter and attach it to the listview..
public class FragmentDetails extends Fragment {
private List<Details>detailsList = new ArrayList<>();
private ListView mListView;
private DetailsAdapter adapter;
public FragmentDetails() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_details, container, false);
//get an instance of our listview
mListView = (ListView)rootView.findViewById(R.id.frag_details_listView);
//Get the data here!!
MyDBHandler dbHandler = new MyDBHandler(getActivity().getApplicationContext());
detailsList = dbHandler.getAllDetails();
//Initiate our adapter
adapter = new DetailsAdapter(getActivity().getApplicationContext(), R.layout.row_details, detailsList);
//set adapter to the listview
if(adapter != null){
mListView.setAdapter(adapter);
}
return rootView;
}
}
NOTE !!
In your MyDbHandler class include another constructor which takes only the context as a parameter like so :
public MyDBHandler(Context context){
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
Your good to go ..run the project
With this procedure you can create any type of listviews in android
回答2:
Create a fragment that extends ListFragment
and implements LoaderManager.LoaderCallbacks<Cursor>
. Like below:
public class MyFragment extends ListFragment implements
LoaderManager.LoaderCallbacks<Cursor> {}
Implement the methods onCreateLoader()
, onLoadFinished()
and onLoaderReset()
.
In the onActivityCreated()
method of your fragment, call getLoaderManager().initLoader()
method.
You can remove getAllDetails()
method from dbhelper and have it as a query in onCreateLoader()
method in the fragment.
For more details, check below links:
https://developer.android.com/training/load-data-background/setup-loader.html
https://developer.android.com/training/load-data-background/handle-results.html
回答3:
I suggest you use Recycler view, its more efficient.The RecyclerView widget is part of the v7 support library. You need to import the recyclerview library(refer to this link)
Refer to this tutorial for more details and better understanding recyclerview.
To start, within your mainActivity code create recyclerview and a customAdapter as below:
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.my_recycler_view);
RecyclerView.LayoutManager mLayoutManager;
dbHelper = new DBHelper(getActivity().getApplicationContext());
list = MyDBHandler.getAllDetails();
mLayoutManager = new LinearLayoutManager(getActivity().getApplicationContext());
recyclerView.setLayoutManager(mLayoutManager);
c = new CustomAdapter(list); //send the list to the adapter
recyclerView.setAdapter(c);
Within your main Layout code you should have this to show the recyclerview:
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:scrollbars="vertical"/>
Now you need to create a custom adapter to show the contents of Details Object. Please note that in the earlier code of the Activity the CustomAdapter is called and passed the list of Details object. We need to use this to show it in the view.
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.viewHolder> {
List<Details> records;
public class viewHolder extends RecyclerView.ViewHolder {
public TextView firstname, surname;
public viewHolder(View v) {
super(v);
firstname = (TextView) v.findViewById(R.id.first);
surname = (TextView) v.findViewById(R.id.last);
}
}
public CustomAdapter(List<Details> records) {
this.records = records;
}
@Override
public viewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.listrow, parent, false);
return new viewHolder(v);
}
@Override
public void onBindViewHolder(final viewHolder holder, final int position) {
Details rec = list.get(position);
holder.firstname.setText(rec.getFirstname());
holder.lastname.setText(rec.getSurname);
}
}
Now you need a layout file for the customadapter, create as below:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical">
<TextView
android:id="@+id/first"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="16dp"
android:textColor="#CC000000"
android:textSize="16sp"
android:text=""/>
<TextView
android:id="@+id/last"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="16dp"
android:textColor="#CC000000"
android:textSize="16sp"
android:text=""/>
</LinearLayout>
来源:https://stackoverflow.com/questions/28717017/using-sqlite-cursor-to-output-table-contents-in-listview-in-a-fragment