It's small Android 2.2 test application using the Compatibility Package. I am trying to update the textview on another fragment on another activity on list item selection. But problem is that everytime the first click returns nullpointer exception and only on the second try its text is changing. I would like to know why is the happening and what is a good solution.
ListActivity:-
public class ListActivity extends FragmentActivity implements
ListFragment.OnItemSelectedListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list);
}
#Override
public void onItemSelected(int index) {
// TODO Auto-generated method stub
// Check to see if there is a frame in which to embed the
// detail fragment directly in the containing UI.
View detailsFrame = findViewById(R.id.detailcontainer);
if (detailsFrame != null
&& detailsFrame.getVisibility() == View.VISIBLE) {
DetailFragment detailFragment = (DetailFragment) getSupportFragmentManager()
.findFragmentById(R.id.detailcontainer);
if (detailFragment == null) {
detailFragment = new DetailFragment();
}
// Execute a transaction, replacing any existing fragment
// with this one inside the frame.
getSupportFragmentManager().beginTransaction()
.replace(R.id.detailcontainer, detailFragment).commit();
detailFragment.setTextView(index);
} else {
// Otherwise we need to launch a new activity to display
Intent intent = new Intent(this, DetailActivity.class);
intent.putExtra("index", index);
startActivity(intent);
}
}
}
ListFragment :-
public class ListFragment extends Fragment {
private OnItemSelectedListener listener;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
String[] countries = new String[] { "India", "Pakistan", "Sri Lanka",
"China", "Bangladesh", "Nepal", "Afghanistan", "North Korea",
"South Korea", "Japan" };
View view = inflater.inflate(R.layout.list_fragment, container, false);
ListView listView = (ListView) view.findViewById(R.id.listView);
// Populate list
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
this.getActivity(), android.R.layout.simple_list_item_1,
countries);
listView.setAdapter(adapter);
// operation to do when an item is clicked
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(getActivity(), "ListItem Number " + position,
Toast.LENGTH_SHORT).show();
listener.onItemSelected(position);
}
});
return view;
}
// Container Activity must implement this interface
public interface OnItemSelectedListener {
public void onItemSelected(int index);
}
// To ensure that the host activity implements this interface
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity instanceof OnItemSelectedListener) {
listener = (OnItemSelectedListener) activity;
} else {
throw new ClassCastException(activity.toString()
+ " must implemenet ListFragment.OnItemSelectedListener");
}
}
public void operation(int index) {
listener.onItemSelected(index);
}
}
Detail Activity :-
public class DetailActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DetailFragment details;
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
finish();
return;
}
if (savedInstanceState == null) {
// During initial setup, plug in the details fragment.
details = new DetailFragment();
details.setArguments(getIntent().getExtras());
getSupportFragmentManager().beginTransaction()
.add(android.R.id.content, details).commit();
}
Bundle extras = getIntent().getExtras();
int index = extras.getInt("index");
try {
details = (DetailFragment) getSupportFragmentManager()
.findFragmentById(R.id.detailfragment);
details.setTextView(index);
} catch (NullPointerException ex) {
ex.getStackTrace();
}
}
}
DetailFragment :-
public class DetailFragment extends Fragment {
String[] capitals;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (container == null)
return null;
capitals = new String[] { "delhi", "karachi", "colombo", "beijing",
"dhaka", "katmandu", "Afghanistan", "pyongyang", "seoul",
"tokyo" };
View v = inflater.inflate(R.layout.detail_fragment, container, false);
return v;
}
public void setTextView(int index) {
try {
TextView view = (TextView) getView().findViewById(R.id.detailView);
view.setText(capitals[index]);
} catch (NullPointerException ex) {
ex.getStackTrace();
}
}
public int getShownIndex() {
return getArguments().getInt("index", 0);
}
}
Update:- I am adding the detailFragment 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="match_parent"
android:background="#color/lightblue"
android:orientation="vertical" >
<TextView
android:id="#+id/detailView"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="#string/detail_frag" />
</RelativeLayout>
Instead of doing details.setTextView(index) in DetailActivity , set the value of TextView in the DetailFragment's onActivityCreated while passing the value to be set in fragments setArgument method in the Detailactivity...
DetailsFragment details = new DetailsFragment();
details.setArguments(getIntent().getExtras()); // pass the value of text view here
And In fragment onActivityCreated get that value via getArguments() and set it in textview..
EDIT for sending value Selected to loaded Fragment
In List Activity
detailFragment = getFragmentbyTag
if(detailFragment == null)
Create Fragment and Add it and Set Arguments here as well
else
detailFragment.setTextView(value); // fragment already loaded no need to set arguments
if you want to replace each time and not add once and use the added/loaded fragment , use setarguments each time.... and remove previous fragment and add new fragment( with Arguments) But added once and reused is preferred instead of removing and adding/ replacing on each click
Updated2
Just initialize your TextView in onCreateView() no need to create DetailFragment with parametrize just put in comment
Check the details object whether its null or not if null initialize as you do and then called setTextView()
if(details==null)
details = new DetailFragment(index);
else
setTextView(index);
TextView inside onCreateView() and used their
public class DetailFragment extends Fragment {
String[] capitals;
int index;
private TextView textView;
public DetailFragment(int index){
this.index = index;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (container == null)
return null;
capitals = new String[] { "delhi", "karachi", "colombo", "beijing",
"dhaka", "katmandu", "Afghanistan", "pyongyang", "seoul",
"tokyo" };
View v = inflater.inflate(R.layout.detail_fragment, container, false);
// Initialize textView
textView = (TextView) v.findViewById(R.id.detailView);
setTextView(index); // set value value here for first time
return v;
}
public void setTextView(int index) {
if(textView!=null)
textView.setText(capitals[index]);
}
public int getShownIndex() {
return getArguments().getInt("index", 0);
}
}
did you tried using this
TextView view = (TextView) findViewById(R.id.detailView);
instead of this
TextView view = (TextView) getView().findViewById(R.id.detailView);
Related
I'm passing value from adapter to fragment class,
Here adapter class,
rbFolder.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
if(selected != null)
{
selected.setChecked(false);
String meee = data.get(position).getTypeId();
System.out.println("*******so*inside********"+meee);
}
rbFolder.setChecked(true);
String so = data.get(position).getTypeId();
System.out.println("*******so*********"+so);
selected = rbFolder;
System.out.println("********selected*******"+selected);
Fragment homepage = new Fragment();
FragmentTransaction fragmentManager =((FragmentActivity)context).getSupportFragmentManager()
.beginTransaction();
Bundle bundle=new Bundle();
bundle.putString("name", so); //key and value
System.out.println("*****venki***meee*******"+so);
homepage.setArguments(bundle);
// fragmentManager.replace(R.id.content_frame, homepage);
fragmentManager.addToBackStack(null);
fragmentManager.commit();
}
});
In this, putstring working well but in my fragemt class i didnt receive values. I'm trouble in this place.
Here Fragmnet class,
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (rootview != null) {
ViewGroup parent = (ViewGroup) rootview.getParent();
if (parent != null)
parent.removeView(rootview);
}
try {
rootview = inflater.inflate(R.layout.homepage, container, false);
} catch (InflateException e) {
/* map is already there, just return view as it is */
}
Here I get values from adapter but this is not working,
Bundle bundle = ((Activity)context).getIntent().getExtras();
if (bundle != null)
{
String strtext=getArguments().getString("name");
System.out.println("*******strtext*********"+strtext);
}
return rootview;
}
First of All you have to call your HomeFragment() instead of fragment like that
Fragment homepage = new HomeFragment();
FragmentTransaction fragmentManager =((FragmentActivity)context).getSupportFragmentManager()
.beginTransaction();
Bundle bundle=new Bundle();
bundle.putString("name", so); //key and value
homepage.setArguments(bundle);
fragmentManager.replace(R.id.content_frame, homepage);
fragmentManager.addToBackStack(null);
fragmentManager.commit();
When you want to get value inside home fragment then
if( getArguments() != null)
String strtext = getArguments().getString("name");
follow steps one by one, copy Context context;
List rowItems;
onItemClickListner onItemClickListner; and pest into your adapter
then copy two constructor methods and then pest on item click method to pass data to fragment.
in your fragment class set adapter item on click listener code in this way...
madapter.setOnClickListener(new MyListAdapterTemp.onItemClickListner() {
#Override
public void onClick(String str) {
}
});
Context context;
List<String> rowItems;
onItemClickListner onItemClickListner;
public MyListAdapterTemp(Context context, List<String> items) {
this.context = context;
this.rowItems = items;
}
public void setOnClickListener(onItemClickListner onItemClickListner) {
this.onItemClickListner = onItemClickListner;
}
holder.txtDesc.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onItemClickListner.onClick(rowItems.get(position).toString().trim().toUpperCase());
}
});
this method works fine on my app and for activity to fragment , fragment to activity we can pass data using interface but not this
way this is best and proper way to perform this type of operations.
Fragment homepage = new Fragment();
this is supposed to be your Fragment class name -
Fragment homepage = new MyFragment();
And Your Fragment be like -
public class MyFragment extends Fragment {
#Override
public void onCreate(...) {
Bundle b = getArguments(); //To get extras from Fragment
}
}
Other than that,
Bundle bundle = ((Activity)context).getIntent().getExtras();
This is used for an Activity. For Fragment you have to use getArguments()
MainActivity on startup add a fragment layout to Relativeview, then i send a data to fragment to add it to ExpandablelistView but my app shows me error that couldn't recognize ExpandablelistView.
MainActivity:
public class MainActivity extends AppCompatActivity implements FragmentAddCatergory.onClickButtonListener {
private FragmentManager manager;
private FragmentTransaction transactionShowList;
private FragmentTransaction transactionAddCatergory;
private FragmentAddCatergory addCatergory;
private FragmentShowCategory showCategory;
private boolean addcategory;
private TextView txtAddCategory;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
manager = getFragmentManager();
transactionShowList = manager.beginTransaction();
showCategory = new FragmentShowCategory();
addCatergory=new FragmentAddCatergory();
transactionShowList.add(R.id.Fragment_container, showCategory);
transactionShowList.commit();
addcategory=false;
txtAddCategory = (TextView) findViewById(R.id.txtaddcategory);
txtAddCategory.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ChangeFragment();
}
});
}
public void ChangeFragment(){
transactionAddCatergory=manager.beginTransaction();
if (addcategory){
transactionAddCatergory.replace(R.id.Fragment_container,addCatergory);
txtAddCategory.setText("Do you want to see your List?Show me!");
addcategory=false;
}else{
transactionAddCatergory.replace(R.id.Fragment_container,showCategory);
txtAddCategory.setText("Do you want to add a Category?Create One");
addcategory=true;
}
transactionAddCatergory.commit();
}
#Override
public void ClickButton(String group, String child) {
FragmentShowCategory a=new FragmentShowCategory();
a.showExpand(this,group,child);
}}
in last above code i make object from first fragment and send a data and in below code is code of first fragment
public class FragmentShowCategory extends Fragment {
private View view;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
view = inflater.inflate(R.layout.activity_expandable_list_view, container, false);
return view;
}
public void showExpand(Context context, String g, String c) {
Toast.makeText(context, g + " is " + c, Toast.LENGTH_LONG).show();
HashMap<String, List<String>> carsDetails = DataProvider.getInfo(g, c);
List<String> carsBrands = new ArrayList<String>(carsDetails.keySet());
ItemClass adapter = new ItemClass(context, carsDetails, carsBrands);
ExpandableListView list = (ExpandableListView) view.findViewById(R.id.expandList);
list.setAdapter(adapter);
}}
but when i ran my app, i get error that i don't know why in line of:
ExpandableListView list = (ExpandableListView) view.findViewById(R.id.expandList);
i'd appreciate to help me.
Your fragment's view hierarchy is not inflated automatically just because you created an instance of your fragment, as you do in ClickButton. The onCreateView() method that has to be called first in order to inflate your views is part of the fragment's lifecycle. You should let Android instantiate your fragment, and acquire it's instance through the FragmentManager.
This tutorial explains basics about fragments very well.
I'm new to Android Developing and i'm trying to create a clickable ListView. And when you click on any element of it, it shows a fragment with the text from elememnt. But it doesn't seem to work becouse you can't commit FragmentTransaction twice. And without first commiting you can't add a fragment to fragment contrainer and therefore you can't change the text of Fragment=(
Here's my Activity Code:
public class MyActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
final FragmentManager fManager = getFragmentManager();
final FragmentTransaction fTransaction = fManager.beginTransaction();
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
Random rand = new Random(); // Randomizer
ArrayList Array = new ArrayList(5); // Array
for(int i = 0; i < 8; i++)
{
Array.add(rand.nextInt(30));
}
ArrayAdapter<String> MyAdapter =
new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, Array);
final ListView listview = (ListView)findViewById(R.id.MyList);
listview.setAdapter(MyAdapter);
final MyFragment MyFrag = new MyFragment();
fTransaction.add(R.id.FragmentContainer, MyFrag);
fTransaction.commit();
AdapterView.OnItemClickListener itemListener = new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
String Selection = parent.getItemAtPosition(position).toString();
MyFrag.SetText(Selection);
fTransaction.remove(MyFrag);
fTransaction.add(R.id.FragmentContainer, MyFrag);
fTransaction.commit();
}
};
listview.setOnItemClickListener(itemListener);
}
I've tried to use "replace" but the result was the same...
It says:
" java.lang.IllegalStateException: commit already called "
Here is my fragment code:
public class MyFragment extends Fragment
{
TextView textView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View myInflatedView = inflater.inflate(R.layout.fragment_my_first, container, false);
textView = (TextView) myInflatedView.findViewById(R.id.MyFragView);
if (textView == null)
{
System.out.println("myInflatedView == null");
}
return inflater.inflate(R.layout.fragment_my_first, null);
}
#Override
public void onAttach( Activity activity)
{
super.onAttach(activity);
Toast.makeText(getActivity(), "You have attached a fragment",
Toast.LENGTH_SHORT).show();
}
public void SetText(String text)
{
if(textView == null)
{
System.out.println("textView == null");
}
textView.setText(text);
}
}
Sry for a huge pack of hard-reading code=(
You have to initiate a new transaction, not use the previous because you have already commit it.
well, you should create new FragmentTransaction inside the onItemClick which will remove the old fragment
I have a list fragment on the left of another fragment and is essentially the standard click an item and update the right fragment pattern. When they click an item in the list fragment they are choosing the news article category and I need to keep whatever one is selected when they rotate the device. How do I do that? My current code doesn't work.
My code is as follows:
public class SideMenuFragment extends ListFragment {
ArrayList<SideItem> sideItems;
SideAdapter sideAdapter;
public SideMenuFragment() {
this.setRetainInstance(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.list, null);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
sideItems = new ArrayList<SideItem>();
...add bunch of items
sideAdapter = new SideAdapter(getActivity(), sideItems);
getListView().setVerticalScrollBarEnabled(false);
setListAdapter(sideAdapter);
if (savedInstanceState != null) {
sideAdapter.setSelectedItem(savedInstanceState.getInt("sidePosition"));
sideAdapter.notifyDataSetChanged();
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("sidePosition", sideAdapter.getSelectedItem());
}
#Override
public void onListItemClick(ListView lv, View v, int position, long id) {
if (sideAdapter.getSelectedItem() != position) {
sideAdapter.setSelectedItem(position);
sideAdapter.notifyDataSetChanged();
}
switch (position) {
...switch the fragment depending on position.
}
}
// the meat of switching the above fragment
private void switchFragment(Fragment fragment, String title) {
if (getActivity() == null)
return;
if (getActivity() instanceof HomeActivity) {
HomeActivity a = (HomeActivity) getActivity();
a.switchContent(fragment, title);
}
}
}
First, add your Fragment in xml if the Activity layout.
In Activity onCreate
getFragmentManager().findFragmentById(R.id.youtfragmentid).setRetainInstance(true)
This means that the fragment will not be recreated on activity recreate.
Don't change your ListView in onActivityCreated - because it will be rebuilt every time orientation changes. If you set a new adapter - the states of children will be reseted.
Add checking for null or a boolean flag that the view already was created.
Next time onActivityCreated gets called, your list adapter should not change
if (sideAdapter == null) {
sideAdapter = new SideAdapter(getActivity(), sideItems);
getListView().setVerticalScrollBarEnabled(false);
setListAdapter(sideAdapter);
}
Also, don't create new view in onCreateView instead use previously created one.
private View v;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (v == null) {
v = inflater.inflate(R.layout.list, null);
} else {
// detatch from container and return the same view
((ViewGroup) getListView().getParent()).removeAllViews();
}
return v;
}
The Android 4.1 ActionBar provides a useful navigation mode as a list or tab. I am using a SpinnerAdapter to select from three fragments to be displayed in view android.R.id.content.
The onNavigationItemSelected() listener then inflates each fragment to the view and adds it to the back stack using FragmentTransaction.addToBackStack(null).
This all works as advertised, but I don't know how to update the ActionBar to reflect the current back stack. Using the ActionBar.setSelectedNavigationItem(position) works but also triggers a new OnNavigationListener() which also creates another FragmentTransaction (not the effect I want). The code is shown below for clarification. Any help with a solution is appreciated.
public class CalcActivity extends Activity {
private String[] mTag = {"calc", "timer", "usage"};
private ActionBar actionBar;
/** An array of strings to populate dropdown list */
String[] actions = new String[] {
"Calculator",
"Timer",
"Usage"
};
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
// may not have room for Title in actionbar
actionBar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
actionBar.setListNavigationCallbacks(
// Specify a SpinnerAdapter to populate the dropdown list.
new ArrayAdapter<String>(
actionBar.getThemedContext(),
android.R.layout.simple_list_item_1,
android.R.id.text1,
actions),
// Provide a listener to be called when an item is selected.
new NavigationListener()
);
}
public class NavigationListener implements ActionBar.OnNavigationListener {
private Fragment mFragment;
private int firstTime = 0;
public boolean onNavigationItemSelected(int itemPos, long itemId) {
mFragment = getFragmentManager().findFragmentByTag(mTag[itemPos]);
if (mFragment == null) {
switch (itemPos) {
case 0:
mFragment = new CalcFragment();
break;
case 1:
mFragment = new TimerFragment();
break;
case 2:
mFragment = new UsageFragment();
break;
default:
return false;
}
mFragment.setRetainInstance(true);
}
FragmentTransaction ft = getFragmentManager().beginTransaction();
if (firstTime == 0) {
firstTime++;
ft.add(android.R.id.content, mFragment, mTag[itemPos]);
} else {
ft.replace(android.R.id.content, mFragment, mTag[itemPos]);
ft.addToBackStack(null);
}
ft.commit();
Toast.makeText(getBaseContext(), "You selected : " +
actions[itemPos], Toast.LENGTH_SHORT).show();
return true;
}
}
public static class CalcFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_calc, container, false);
return v;
}
}
public static class TimerFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_timer, container, false);
return v;
}
}
public static class UsageFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_usage, container, false);
return v;
}
}
You could do something like this:
Create a boolean to track when you are selecting a navigation item based on the back button:
private boolean mListIsNavigatingBack = false;
Override onBackPressed, in the override, check if the backstack has items, if so handle yourself, if not call the superclass:
public void onBackPressed() {
if(getFragmentManager().getBackStackEntryCount() > 0){
mListIsNavigatingBack = true;
//You need to get the previous index in the backstack through some means
//possibly by storing it in a stack
int previousNavigationItem = ???;
getActionBar().setSelectedNavigationItem(previousNavigationItem);
}
else{
super.onBackPressed();
}
}
Inside NavigationListener, handle the mListIsNavigatingBack state, manually pop the back stack and unset the state:
if(mListIsNavigatingBack){
if(fm.getBackStackEntryCount() > 0){
fm.popBackStack();
}
mListIsNavigatingBack = false;
}