I'm would like to make my Android application use Fragment Transactions, so that I can
switch between various Fragments display their associated lists. My application
worked fine prior to attempting to convert to just Fragment Transactions. In my initial activity_main.xml,
I removed the android:name="com.birdsall.peter.chap.ChapterFragment", it is my understanding you can't use xml defined fragments
with fragment transactions.
1) I can't seem to get the .add() within
getSupportFragmentManager correct with it's parameters, even with code from a working example.
I have also attempted to use the newer version of FragmentTransactions to no avail.
I even took a working example of code using getSupportFragmentManager / FragmentTransactions,
modified to use the names I had, it worked. I then imported that code into my application
and it fails on the .add() syntax. I'm somewhat new to Android development and can't
pinpoint where I'm going wrong.
Here is my main xml layout for the FrameLayout
activity_main.xml
<?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" >
<FrameLayout
android:id="#+id/fragment_container"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
android:id="#+id/chapterfragments"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- Removed this from above <fragment> block to enable Fragment Transaction
android:name="com.birdsall.peter.chap.ChapterFragment" -->
</LinearLayout>
Here is the xml layout for the ChapterFragment ListView
chapterfragment.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView android:id="#+id/chapterlist" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_alignParentLeft="true" />
</RelativeLayout>
Here is the layout for the details of the list
chapter_info.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">
<TextView android:id="#+id/chapter1" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_alignParentTop="true"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView android:id="#+id/textViewLiteral" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_alignParentTop="true"
android:layout_toRightOf="#+id/chapter1" android:text=" - "
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView android:id="#+id/chaptertitle1" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_alignParentTop="true"
android:layout_toRightOf="#+id/textViewLiteral"
android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
Here is the MainActivity.java that I have modified. I left the 'setContentView(R.layout.activity_main);' as I thought I needed to create a view (even though it empty) to add the fragment view to.
package com.birdsall.peter.chap;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
public class MainActivity extends FragmentActivity implements ChapterFragment.ChapterSelectedListener {
private static final String TAG = "Main_Activity";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "Starting ...");
setContentView(R.layout.activity_main);
if (findViewById(R.id.fragment_container) != null) {
if (savedInstanceState != null) {
return;
}
// Create an instance of ExampleFragment
ChapterFragment firstFragment = new ChapterFragment();
// Add the fragment to the 'fragment_container' FrameLayout
getSupportFragmentManager().beginTransaction()
**.add**(R.id.fragment_container, firstFragment).commit();
Log.i(TAG, "Ending ...");
}
} ...
Here is my ChapterFragment.java
package com.birdsall.peter.chap;
import android.app.Activity;
import android.app.Fragment;
import android.app.LoaderManager;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.Toast;
public class ChapterFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
ChapterSelectedListener mCallback;
// Container Activity must implement this interface
public interface ChapterSelectedListener {
public void onChapterSelected(String position, int rowId);
}
public SimpleCursorAdapter dataAdapter;
private static final String TAG = "ChapterFragment";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState ) {
View listview = inflater.inflate(R.layout.activity_main,null);
ListView mList =(ListView)listview.findViewById(R.id.chapterlist);
// The desired columns to be bound
String[] columns = new String[] {
TDAdb.COL_CHAPTER,
TDAdb.COL_CHAPTERTITLE};
// the XML defined views which the data will be bound to
int[] to = new int[] {
R.id.chapter1,
R.id.chaptertitle1,
};
// create an adapter from the SimpleCursorAdapter
dataAdapter = new SimpleCursorAdapter(
getActivity(),
R.layout.chapter_info,
null,
columns,
to,
0);
mList.setAdapter(dataAdapter);
//Ensures a loader is initialized and active.
getLoaderManager().initLoader(0, null, this);
return listview;
}
#Override
public void onStart() {
super.onStart();
Log.i(TAG, " onStart");
displayListView();
Log.i(TAG, " end of onStart");
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (ChapterSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement ChapterSelectedListener");
}
}
#Override
public void onResume() {
super.onResume();
//Starts a new or restarts an existing Loader in this manager
Log.i(TAG, " onResume");
getLoaderManager().restartLoader(0, null, this);
}
private void displayListView() {
Log.i(TAG, " Starting displayListView");
// The desired columns to be bound
String[] columns = new String[] {
TDAdb.COL_CHAPTER,
TDAdb.COL_CHAPTERTITLE};
// the XML defined views which the data will be bound to
int[] to = new int[] {
R.id.chapter1,
R.id.chaptertitle1,
};
// create an adapter from the SimpleCursorAdapter
dataAdapter = new SimpleCursorAdapter(
getActivity(),
R.layout.chapter_info,
null,
columns,
to,
0);
// get reference to the ListView
ListView listView = (ListView) getView().findViewById(R.id.chapterlist);
// Assign adapter to ListView
listView.setAdapter(dataAdapter);
//Ensures a loader is initialized and active.
getLoaderManager().initLoader(0, null, this);
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> listView, View view,
int position, long id) {
// Get the cursor, positioned to the corresponding row in the result set
Cursor cursor = (Cursor) listView.getItemAtPosition(position);
String chaptervalueselected =
cursor.getString(cursor.getColumnIndexOrThrow(TDAdb.COL_CHAPTER));
mCallback.onChapterSelected(chaptervalueselected, position);
Toast.makeText(getActivity(), "Chapter " + chaptervalueselected, Toast.LENGTH_SHORT).show();
// starts a new Intent to update/delete a Chapter
// pass in row Id to create the Content URI for a single row
//Intent chapterEdit = new Intent(getBaseContext(), ChapterEdit.class);
//Bundle bundle = new Bundle();
//bundle.putString("mode", "update");
//bundle.putString("rowId", rowId);
//chapterEdit.putExtras(bundle);
//startActivity(chapterEdit);
}
});
}
// This is called when a new Loader needs to be created.
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Log.i(TAG, " onCreateLoader");
String[] projection = {
TDAdb.KEY_ROWID,
TDAdb.COL_CHAPTER,
TDAdb.COL_CHAPTERTITLE};
CursorLoader cursorLoader = new CursorLoader(getActivity(),
TDAProvider.CONTENT_URI, projection, null, null, null);
return cursorLoader;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
Log.i(TAG, " ononLoadFinished");
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
dataAdapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
Log.i(TAG, " onLoaderReset");
dataAdapter.swapCursor(null);
}
}
If you are already using the add method of FragmentTransaction you must not include the <fragment tag in your layout. What if you just left your main activity XML like this:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
In chapterFragment.java change your import
import android.app.Fragment
To
import android.support.v4.app.Fragment
Related
I have an Activity that lays out ListFragments side by side (as many as a user wants) in a HorizontalScrollView upon choosing an option in the ActionBar.
Each ListFragment item contains TextViews and a Button. A SimpleAdapter populates data for each item in every ListFragment.
The issue I am facing now is that the buttons in each list item do not respond to clicks. The layout can be summarized as follows: Button inside a ListFragment inside a FragmentActivity, going from the innermost child element to the parent at the root view.
I have spent many hours on this problem and I am unable to arrive at a solution to make the buttons respond to clicks. Some of the approaches I have used include obtaining the button's view and attaching onClickListeners, 2) implementing the OnClickListener interface for the ListFragment. I am also aware of the onInterceptTouchEvent method for a ViewGroup class, however my lack of experience with Android prevents me from arriving at a solution. Any guidance or direction towards solving this problem would be most appreciated.
Here is the code for the ListFragment:
package com.example.androidlistfragmenttest;
import java.util.ArrayList;
import java.util.HashMap;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.SimpleAdapter;
public class MyFragment extends ListFragment {
private ArrayList<HashMap<String,String>> arraylist;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_layout, container, false);
Button button = (Button) view.findViewById(R.id.button);
button.setOnClickListener(new OnClickListener(){
//THIS DOES NOT PRINT IN LOGCAT. BUTTON DOES NOT RESPOND TO CLICKS.
#Override
public void onClick(View arg0) {
Log.v("GODZILLA","ATOMIC BREATH");
}
});
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
arraylist = dataGenerator();
SimpleAdapter adapter = new SimpleAdapter(getActivity().getApplicationContext(), arraylist, R.layout.fragment_layout,new String[]{"KEY"},new int[]{R.id.text_id});
setListAdapter(adapter);
}
/*
* Method to populate an adapter's data list.
*/
public ArrayList<HashMap<String,String>> dataGenerator(){
HashMap<String,String> hashMap1 = new HashMap<String,String>();
hashMap1.put("KEY", "A");
HashMap<String,String> hashMap2 = new HashMap<String,String>();
hashMap2.put("KEY", "B");
HashMap<String,String> hashMap3 = new HashMap<String,String>();
hashMap3.put("KEY", "C");
HashMap<String,String> hashMap4 = new HashMap<String,String>();
hashMap4.put("KEY", "D");
HashMap<String,String> hashMap5 = new HashMap<String,String>();
hashMap5.put("KEY", "E");
ArrayList<HashMap<String,String>> arraylist = new ArrayList<HashMap<String,String>>();
arraylist.add(hashMap1);
arraylist.add(hashMap2);
arraylist.add(hashMap3);
arraylist.add(hashMap4);
arraylist.add(hashMap5);
return arraylist;
}
} //End of MyFragment
And this is the code for the Activity containing the Fragment(s):
package com.example.androidlistfragmenttest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Stack;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.NavUtils;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends FragmentActivity {
private Stack<String> tagStack;
private Integer last_tag_number;
public MainActivity(){
last_tag_number = new Integer("0");
tagStack = new Stack<String>();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.add_fragment:
addColumn();
return true;
case R.id.remove_column:
removeColumn();
return true;
case android.R.id.home:
// This ID represents the Home or Up button. In the case of this
// activity, the Up button is shown. Use NavUtils to allow users
// to navigate up one level in the application structure. For
// more details, see the Navigation pattern on Android Design:
//
// http://developer.android.com/design/patterns/navigation.html#up-vs-back
//
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
/*
* This method adds a fragment to the screen
*/
public void addColumn(){
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
MyFragment fragment = new MyFragment();
fragmentTransaction.add(R.id.fragment_activity, fragment,tagGenerator());
fragmentTransaction.commit();
}
/*
* This method removes a fragment from the screen
*/
public void removeColumn(){
if(tagStack.size() != 0){
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag(tagStack.pop());
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.remove(fragment);
fragmentTransaction.commit();
}
}
/*
* This function generates tags for each fragment that is displayed on the screen
* The tags pose as unique identifiers for each fragment
*/
public String tagGenerator(){
Integer tag_number;
if(last_tag_number.intValue() == 0){
tag_number = last_tag_number;
int temp = last_tag_number.intValue();
temp+=1;
last_tag_number = Integer.valueOf(temp);
}
else{
tag_number = new Integer(last_tag_number.intValue());
int temp = last_tag_number.intValue();
temp+=1;
last_tag_number = Integer.valueOf(temp);
}
String tag = tag_number.toString();
tagStack.push(tag);
return tag;
}
} //End of MainActivity
As well as the layouts for the FragmentActivity:
<?xml version="1.0" encoding="utf-8"?>
<HorizontalScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:id="#+id/fragment_activity"
android:layout_width="fill_parent"
android:layout_height = "fill_parent"
android:orientation = "horizontal"
android:gravity="center"
>
</LinearLayout>
</HorizontalScrollView>
And the ListFragment:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="300dp"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_weight="1"
android:layout_margin="5dp"
android:descendantFocusability="blocksDescendants" >
<ListView android:id="#id/android:list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal"
android:layout_gravity="center" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/label"
android:layout_gravity="start"
/>
<TextView
android:id="#+id/text_id"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="end"
/>
</LinearLayout>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="horizontal" >
<Button
android:id="#+id/button"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="#string/button"
android:layout_margin="2dp"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
/>
</LinearLayout>
</LinearLayout>
The problem is that the ListItem is consuming the click and it is not getting passed to the button below.
You need to set the list items to inactive and handle the clicks yourself. In your custom Adapter add the following methods to disable the items (They override methods from BaseAdapter):
#Override
public boolean areAllItemsEnabled() {
return false;
}
#Override
public boolean isEnabled(int position) {
return false;
}
Edit: Here is a related question that may offer a better solution, it depends on your design.
I'm trying to call a fragment from my activity. The onCreate and onCreateView methods are being called. However, the UI for the fragment is not being displayed. I don't see any errors in logcat either.
Basically this is what I want to do. Upon clicking the imagebutton in activity.xml, I call the fetchNotifications() method, which in turn calls an asynctask to do some work and then call my fragment. In my fragment, I create a dynamic listview containing multiple textviews.
Here are my codes:
activity_home_screen.xml (my activity layout):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageButton
android:id="#+id/imageButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="46dp"
android:src="#drawable/read_msg"
android:onClick="fetchNotifications"/>
</RelativeLayout>
HomeScreen.java (my activity class):
public class HomeScreen extends FragmentActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home_screen);
}
//Method called from button onClick
public void fetchNotifications(View view){
new FetchNotifications().execute("http://x.x.x.x:9000/Android/homescreen/ruby");
}
private class GetNotifications extends AsyncTask<String, String, String>{//some code here}
private class FetchNotifications extends AsyncTask<String, String, String>{
FragmentManager fm;
#Override
protected String doInBackground(String... params) {
try {
URL notifications = new URL(params[0]);
//JSONObject notificationsJSON = new JSONObject(getResponse(notifications).toString());
JSONObject notificationsJSON = new JSONObject(globals.JSONS.NOTIFICATIONS);
if(notificationsJSON!=null){
Globals globals = Globals.getInstance();
globals.setReplyString(notificationsJSON.getJSONArray("notification").toString());
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(new DisplayNotifications(), "Notification");
ft.commit();
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
public StringBuilder getResponse(URL url){//some code here}
}
activity_display_notifications.xml (my fragment_layout):
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.buildingcustomadapter.MainActivity"
tools:ignore="MergeRootFrame">
<ListView
android:id="#+id/notifications_listView"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
</FrameLayout>
single_notification.xml (contains textview which will be added to the listview):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="#+id/notification_TextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="Small Text"
android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
And finally my fragment class (DisplayNotifications.java):
package com.example.projectswipe;
import globals.Globals;
import java.util.ArrayList;
import org.json.JSONArray;
import org.json.JSONException;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
public class DisplayNotifications extends Fragment {
Globals global;
JSONArray notificationsArray;
ArrayList<String> notification = new ArrayList<String>();
ListView l;
public DisplayNotifications() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState)
{
Log.d("Test", "onCreate >>>>>>>>");
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_display_notifications, container, false);
Log.d("Test", "onCreateView >>>>>>>>");
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
try {
global = Globals.getInstance();
notificationsArray = new JSONArray(global.getReplyString());
System.out.println(">>>>>>>>>>"+notificationsArray);
for(int i=0; i<notificationsArray.length(); i++){
notification.add(notificationsArray.getJSONObject(i).getString("msg"));
}
} catch (JSONException e) {
e.printStackTrace();
}
l = (ListView) getView().findViewById(R.id.notifications_listView);
NotificationsAdapter adapter = new NotificationsAdapter(getActivity(), notification);
Log.d("Test", "onActivityCreated >>>>>>>>");
l.setAdapter(adapter);
//l.setOnItemClickListener(this);
//l.setEmptyView(getActivity().findViewById(R.id.emptyProductDeals));
}
}
class NotificationsAdapter extends ArrayAdapter<String>{
Context contex;
ArrayList<String> notifications;
/*
* Create a constructor which calls super(). In super(), we pass context, the singlerow.xml file, and the datasource
*/
NotificationsAdapter(Context c, ArrayList<String> notifications){
super(c, R.layout.single_notification, notifications);
this.contex = c;
this.notifications = notifications;
}
class MyViewHolder{
TextView notification;
MyViewHolder(View v){
notification = (TextView) v.findViewById(R.id.notification_TextView);
}
}
public View getView(int position, View convertView, ViewGroup parent){
View root = convertView;
MyViewHolder holder = null;
if(root==null)
{
LayoutInflater inflater = (LayoutInflater) contex.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
root = inflater.inflate(R.layout.single_notification, parent, false);
holder = new MyViewHolder(root);
root.setTag(holder);
}
else{
holder = (MyViewHolder) root.getTag();
}
holder.notification.setText(notifications.get(position));
return root;
}
}
What am I doing wrong here? I know I'm missing something really basic, but I've been stuck at this thing for hours.
Thanks.
First, your main layout doesn't include a container (usually a FrameLayout) for your fragment and your FragmentTransaction doesn't specify where the fragment view must be attached. The add() method which doesn't take a containerViewId as parameter is reserved for fragments without a user interface, that's why you don't see anything.
Second, you can't call the FragmentManager from a background thread like you do here. It must always be done on the UI thread. If you use an AsyncTask, it means the FragmentTransaction must be done in onPostExecute() and not in doInBackground(). However, make sure your activity is started before you try to commit the fragment transaction or you will get an IllegalStateException. A common way to do this is to cancel the AsyncTask in onStop(), so onPostExecute() won't be called after onStop().
Other potential problems in your code:
Never set the height of a ListView to wrap_content. It may cause glitches and performance problems. Use match_parent, a fixed height, or a weight instead.
Pass the data that your Fragment will show in its Arguments Bundle, or make the Fragment load its own data when it is shown, instead of loading the data in some global variable that your fragment will access later on. Your Fragment cannot rely on the fact that the global variable will be properly set when it starts because the system may kill your application at any moment when it's not visible and restore it later. When the application is restored, the Activity, the Fragment and its arguments bundle will be restored as well, but your global variable will not.
The following ListFragment doesn't display anything other than a blank screen. I know the data are present. I'm not getting errors, just a blank (white) screen. I'm sure it's something silly. I'm fairly new to Android.
package com.pbs.deliverytrack1;
import com.pbs.deliverytrack1.DBContract;
import com.pbs.deliverytrack1.DBHelper;
import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.widget.SimpleCursorAdapter;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class OrderListFragment extends ListFragment {
SimpleCursorAdapter mAdapter;
String TAG = "OrderListFragment";
static final String[] PROJECTION = new String[] {
DBContract.DeliveryOrderTable._ID,
DBContract.DeliveryOrderTable.CUSTOMER,
DBContract.DeliveryOrderTable.ADDRESS };
// Selection criteria
static final String SELECTION = "(("
+ DBContract.DeliveryOrderTable.CUSTOMER + " NOTNULL) AND ("
+ DBContract.DeliveryOrderTable.CUSTOMER + " != '' ) AND ("
+ DBContract.DeliveryOrderTable.DELIVERED_DATETIME + " = '' ))";
private OnOrderSelectedListener listener = null;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String tag = TAG + ".onCreate()";
Log.d(tag,"Fragment created");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
String tag = TAG + ".onCreateView()";
Log.d(tag, "Inflating fragment_order_list - or trying to.");
View view = inflater.inflate(R.layout.fragment_order_list, container,
false);
if (view == null) {
Log.d(tag, "Problem inflating view, returned null");
}
initializeList();
return view;
}
public void onActivityCreated(Bundle bundle) {
super.onActivityCreated(bundle);
String tag = TAG + ".onActivityCreated()";
Log.d(tag,"Parent Activity Created");
}
public interface OnOrderSelectedListener {
public void onOrderSelected(long orderId);
// show detail record you dummy
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
String tag = "OrderListFragment.onAttach()";
Log.d(tag,"Attached!");
if (activity instanceof OnOrderSelectedListener) {
listener = (OnOrderSelectedListener) activity;
} else {
throw new ClassCastException(activity.toString()
+ " must implement MyListFragment.OnItemSelectedListener");
}
} // onAttach()
public void onStart() {
super.onStart();
Log.d(TAG + ".onStart()", "Started!!!");
}
public void onResume() {
super.onResume();
Log.d(TAG + "onResume()", "Resumed!!!");
}
public void onPause() {
super.onPause();
Log.d(TAG + ".onPause()", "Paused");
}
public void onStop() {
super.onStop();
Log.d(TAG + ".onStop()", "Stopped");
}
public void onDestroyView() {
super.onDestroyView();
Log.d(TAG + ".onDestroyView()", "View Destroyed");
}
public void onDestroy() {
super.onDestroy();
Log.d(TAG + ".onDestroy()", "I'm dying!!!!");
}
public void onDetach() {
super.onDetach();
Log.d(TAG + ".onDetach()", "Off with the fragment!");
}
private void initializeList() {
String tag = TAG + ".initilizeList()";
Log.d(tag,"Setting up cursor.");
// for the cursor adapter, specify which columns go into which views
String[] fromColumns = {
DBContract.DeliveryOrderTable._ID,
DBContract.DeliveryOrderTable.CUSTOMER,
DBContract.DeliveryOrderTable.ADDRESS };
int[] toViews = {
R.id.list_view_order_id,
R.id.list_view_customer_field,
R.id.list_view_address_field };
// create an empty adapter we will use to display the loaded data.
DBHelper dbHelper = new DBHelper(getActivity());
Cursor cursor = dbHelper.getAllOrdersCursor();
if (cursor != null) {
cursor.moveToFirst();
Log.d(tag,"Creating SimpleCursorAdapter mAdapter");
mAdapter = new SimpleCursorAdapter(getActivity(),
R.layout.fragment_order_list_row,
cursor,
fromColumns,
toViews,
0);
setListAdapter(mAdapter);
int count = mAdapter.getCount();
Log.d(tag, "Order Record Count = " + count);
if (mAdapter.isEmpty()) {
Log.d(tag, "Alas! mAdapter is empty");
} // end if mAdapter empty
} else { // cursor is null
Log.d(tag,"The cursor is null");
} // if cursor != null
}
}
This fragment is supposed to show a list derived from a SQLite query. The UI I'm building is a simple split list/detail screen on a tablet. I'm building for pre-Honeycomb, so I'm using the support library.
Let me know if you need to see any other parts of the code.
Here's fragment_order_list.xml
<?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="vertical" >
<ListView
android:id="#id/android:list"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:background="#00FF00"
android:drawSelectorOnTop="false" />
<TextView
android:id="#id/android:empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF0000"
android:text="#string/err_no_data" />
</LinearLayout>
Here's the activity the ListFragment is called from:
package com.pbs.deliverytrack1;
import com.pbs.deliverytrack1.OrderListFragment.OnOrderSelectedListener;
import com.pbs.deliverytrack1.DBHelper;
import com.pbs.deliverytrack1.DeliveryOrder;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.Menu;
import android.view.View;
public class MainActivity extends FragmentActivity implements
OnOrderSelectedListener {
private final String TAG = "MainActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String tag = TAG + ".onCreate()";
Log.d(tag, "Setting up database.");
new SetupDatabase().execute(this);
Log.d(tag, "Setting Content View");
setContentView(R.layout.activity_main);
}
#Override
public void onOrderSelected(long orderId) {
String tag = TAG + ".onOrderSelected()";
Log.d(tag,"Creating order detail fragment");
OrderDetailFragment fragment = (OrderDetailFragment) getSupportFragmentManager()
.findFragmentById(R.id.detailFragment);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
Here's activity_main.xml
<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"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".MainActivity"
android:orientation="horizontal"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<fragment
android:id="#+id/action_bar_fragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
class="com.pbs.deliverytrack1.MyActionBarFragment"
tools:layout="#layout/fragment_actionbar" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:orientation="horizontal"
>
<fragment
android:id="#+id/listFragment"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="40"
android:layout_gravity="bottom"
class="com.pbs.deliverytrack1.OrderListFragment" />
<fragment
android:id="#+id/detailFragment"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="60"
class="com.pbs.deliverytrack1.OrderDetailFragment"
tools:layout="#layout/fragment_order_detail" />
</LinearLayout>
</LinearLayout>
MainActivity's LinearLayout is set to:
android:orientation="horizontal"
But you seem to have a "vertical" design in mind, because your first child's width is "match_parent". This means that the ListView is drawn, just off the right side of the screen... Simply change the root layout's orientation.
Hello I'm trying to develop android application that pulls all the image from my phone camera and display in GridView. However, I'm getting the following error:
01-11 09:38:38.890: I/System.out(6766): resolveUri failed on bad bitmap uri: /storage/sdcard0/DCIM/.thumbnails/1347279747819.jpg
In fact I'm getting a lot of this, which is why when I test it on my phone, I can see the scroll bar but no thumbnail displayed (except 2 images)
Here's my code that I thought relevant:
package org.dale.dalegetcontent1;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.SimpleCursorAdapter;
import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.GridView;
public class GetContent1 extends FragmentActivity implements LoaderCallbacks<Cursor>{
/** SimpleCursorAdapter, holds images and layout for the gridview */
SimpleCursorAdapter mAdapter;
#Override
protected void onStart() {
super.onStart();
/** Initializes the Loader */
getSupportLoaderManager().initLoader(0, null, this);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_get_content1);
/** Getting a reference to gridview of the MainActivity layout */
GridView gridView = (GridView) findViewById(R.id.gridview);
/** Create an adapter for the gridview */
/** This adapter defines the data and the layout for the grid view */
mAdapter = new SimpleCursorAdapter(
getBaseContext(),
R.layout.gridview,
null,
new String[] { "_data","_id"} ,
new int[] { R.id.img},
0
);
/** Setting adapter for the gridview */
gridView.setAdapter(mAdapter);
/** Loader to get images from the SD Card */
getSupportLoaderManager().initLoader(0, null, this);
/** Defining item click listener for the grid view */
OnItemClickListener itemClickListener = new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
/** Getting the cursor object corresponds to the clicked item */
Cursor c1 = (Cursor ) arg0.getItemAtPosition(position);
/** Getting the image_id from the cursor */
/** image_id of the thumbnail is same as the original image id */
String id = c1.getString(c1.getColumnIndex("image_id"));
/** Creating a bundle object to pass the image_id to the ImageDialog */
Bundle b = new Bundle();
/** Storing image_id in the bundle object */
b.putString("image_id", id);
/** Instantiating ImageDialog, which displays the clicked image */
ImageDialog img = new ImageDialog();
/** Setting the bundle object to the ImageDialog */
img.setArguments(b);
/** Opening ImageDialog */
img.show(getSupportFragmentManager(), "IMAGEDIALOG");
return ;
}
};
/** Setting itemclicklistener for the grid view */
gridView.setOnItemClickListener(itemClickListener);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_get_content1, menu);
return true;
}
/** A callback method invoked by the loader when initLoader() is called */
#Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
/** Getting uri to the Thumbnail images stored in the external storage */
Uri uri = MediaStore.Images.Thumbnails.getContentUri("external");
System.out.println("Thumb URI: "+uri.toString());
/** Invoking the uri */
return new CursorLoader(this, uri, null, null, null, null);
}
/** A callback method, invoked after the requested content provider returned all the data */
#Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor arg1) {
mAdapter.swapCursor(arg1);
}
#Override
public void onLoaderReset(Loader<Cursor> arg0) {
// TODO Auto-generated method stub
}
}
And my gridview.xml under layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
<ImageView
android:id="#+id/img"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:contentDescription="#string/img_description"
android:padding="10dp"
android:adjustViewBounds="true"
/>
</LinearLayout>
And my activity_get_content1.xml under layout:
<RelativeLayout 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=".GetContent1" >
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/gridview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:numColumns="auto_fit"
android:verticalSpacing="10dp"
/>
</RelativeLayout>
Thanks, happy to provide more info if required
Cheers, Dale
Maybe you should add this permission in the AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
Hi I want to built a android a application with a ListView and if I select a value in this ListView I want to start a other activity with this value. The Problem is I don't know how I can click on a value in my ListView and the other activity get the informations. Here is my Code:
My First Activity:
package de.android.shilfe;
import de.android.shilfe.DatenbankManager;
import de.android.shilfe.R;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import android.app.ListActivity;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
public class MainActivity extends ListActivity {
private SQLiteDatabase mDatenbank;
private DatenbankManager mHelper;
private static final String KLASSEN_SELECT_ROW =
"SELECT _id, name FROM Stundenplan";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHelper.STUNDENPLAN_CREATE = "CREATE TABLE Stundenplan(" +
"_id INTEGER PRIMARY KEY AUTOINCREMENT, "+
"name TEXT NOT NULL)";
mHelper = new DatenbankManager(this);
//what I must do here ??? :(
}
#Override
protected void onPause() {
super.onPause();
mDatenbank.close();
Toast.makeText(this,
getResources().getString(R.string.db_close),
Toast.LENGTH_SHORT).show();
}
#Override
protected void onResume() {
super.onResume();
mDatenbank = mHelper.getReadableDatabase(); //Datenbank öffnen
Toast.makeText(this,
getResources().getString(R.string.db_open),
Toast.LENGTH_SHORT).show();
ladeDaten();
}
private void ladeDaten() {
Cursor KlassenCursor = mDatenbank.rawQuery(KLASSEN_SELECT_ROW, null); // Gibt ein Index zurück
startManagingCursor(KlassenCursor); //Durchläuft diesen
android.widget.SimpleCursorAdapter KlassenAdapter = new android.widget.SimpleCursorAdapter(this,
android.R.layout.simple_list_item_1,
KlassenCursor,
new String[] {"name"},
new int[] {
android.R.id.text1
});
setListAdapter(KlassenAdapter);
}
public void onButtonClick(View view){
EditText et =(EditText) findViewById(R.id.editText1);
ContentValues werte = new ContentValues();
werte.put("name",et.getText().toString());
mDatenbank.insert("stundenplan", null, werte);
ladeDaten();
et.setText("");
}
}
My First Activity Layout xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="5">
<EditText
android:id="#+id/editText1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:hint="#string/create_defaulttext">
<requestFocus />
</EditText>
<Button
android:id="#+id/sf_daten_datenbanken_einfuegen"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/sf_daten_datenbanken_einfuegen"
android:onClick="onButtonClick" />
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1">
<ListView
android:id="#+id/android:list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
</ListView>
<TextView
android:id="#+id/android:empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/tx_daten_datenbank_leer"/>
</LinearLayout>
</LinearLayout>
I try it in a other Application with this code:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
List valueList = new ArrayList<String>();
for(int i=0;i<10;i++)
{
valueList.add("value" + i);
}
ListAdapter adapter = new ArrayAdapter<String>(getApplicationContext(),android.R.layout.simple_list_item_1,valueList);
final ListView lv = (ListView)findViewById(R.id.listview1);
lv.setAdapter(adapter);
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3){
Intent intent = new Intent();
intent.setClassName(getPackageName(), getPackageName() + ".Show_Activity");
intent.putExtra("selected",lv.getAdapter().getItem(arg2).toString());
startActivity(intent);
}
});
}
but in the other Application I fill the ListView by a EditText Control
I need help :(
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id){
Intent intent = new Intent(context, Show_Activity.class);
intent.putExtra("selected", (String) lv.getItemAtPosition(position));
startActivity(intent);
}
});
You already did pass the value to another activity with putExtra
Hey You question is not complete but I am giving this answer as per what I understand. In the onListItemClick() method last parameter of long datatype is id of a row selected. in your case, it will _id of table. If you have 10 rows in your table with id 10 to 20 and displayed the same in listview. Now if you click on 5 item then its id will be 15 which was stored in database. Pass this value to next intent and fetch the details of that row and display in the next activity.
When you use a ListActivity so you can use onListItemClick method.This method will be called when an item in the list is selected.
You can to create a custom list adapter and handle the onClick event in the getView method of the adapter. The item will be available in this method.
public class AccountListAdapter extends ArrayAdapter<Account> {
#Override
public View getView(int position, View convertView, ViewGroup parent){
final Account item = this.getItem(position);
// item = the particular list item you clicked
// layoutItem = layout view passed into the adapter
layoutItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
callActivity(item);
}
});
}
}
While handling the event, create an intent with extras and pass the value(s) you desire via the extras. You can use a simplelistadapter, but this way gives you considerably more control and power!