Layout for tablets using two fragments - android

I am doing a sample project as an excercise and want to display two fragments in the same activity when dealing with tablets(7' and 10').
So what I have so far is this.
As you can see I can display the data of my recyclerview in the left (static) fragment. However the right fragment is empty.
So I have two questions.
1) How to display by default in the right fragment the data of the first row of recyclerview?(ie image and article)
2) How to implement the click listener and update the right fragment?
Here is my code:
MainActivity
public class MainActivity extends AppCompatActivity {
private boolean mTwoPane;
private static final String DETAIL_FRAGMENT_TAG = "DFTAG";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(findViewById(R.id.detailed_match_reports)!=null) {
mTwoPane = true;
if (savedInstanceState == null) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.detailed_match_reports, new DetailedActivityFragment(),DETAIL_FRAGMENT_TAG)
.commit();
}else{
mTwoPane = false;
}
}
}
}
layout/activity_main.xml
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/match_reports"
android:name="theo.testing.androidservices.fragments.MainActivityFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
tools:context="theo.testing.androidservices.activities.MainActivity"
tools:layout="#android:layout/list_content" />
layout-sw600dp/activity_main
<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:baselineAligned="false"
android:divider="?android:attr/dividerHorizontal"
android:orientation="horizontal"
tools:context="theo.testing.androidservices.activities.MainActivity">
<fragment
android:id="#+id/match_reports"
android:name="theo.testing.androidservices.fragments.MainActivityFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
tools:layout="#android:layout/list_content" />
<FrameLayout
android:id="#+id/detailed_match_reports"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="4" />
</LinearLayout>
MainActivityFragment
public class MainActivityFragment extends Fragment {
public static final String TAG = "AelApp";
public static ArrayList<MyModel> listItemsList;
RecyclerView myList;
public static MatchReportsAdapter adapter;
public MainActivityFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
updateMatchReport();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
getActivity().setTitle("Match Report");
View rootView = inflater.inflate(R.layout.fragment_main_activity, container, false);
listItemsList = new ArrayList<>();
myList = (RecyclerView)rootView.findViewById(R.id.listview_match_reports);
final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
myList.setHasFixedSize(true);
myList.setLayoutManager(linearLayoutManager);
adapter = new MatchReportsAdapter(getActivity(), listItemsList);
myList.setAdapter(adapter);
return rootView;
}
public void updateMatchReport(){
Intent i = new Intent(getActivity(), MatchReport.class);
getActivity().startService(i);
}
}

First let me answer the second question. To show a fragment on the right side of the screen add something like this (note how I'm using that id of the frame layout to replace the fragment):
Fragment fragment = new MyRightSideFragment();
FragmentManager fm = getFragmentManager();
fm.beginTransaction().replace(R.id.detailed_match_reports, fragment).commit();
Now, for the first question, you need to implement click listener, you can find an example here.
public class ReactiveAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = (Integer)v.getTag();
showDetail(position);
}
});
}
}
Notice that I'm using the tag of the view to identify the position (so somwhere in your onBindViewHolder code you must set this tag).
public function showDetail(int position) {
... show fragment por position
}
and finally, when your in your OnCreateView or somewhere in your setup code, call showDetail(0).

Related

Error: E/RecyclerView: No adapter attached; skipping layout on Fragments

I am trying to display a RecyclerView on a fragment using FireBase Database, but for some unknown reason its giving me this error: E/RecyclerView: No adapter attached; Im pretty sure im actually attaching the adapter, here's the code:
Main Activity
public class MainActivity extends AppCompatActivity {
Fragment currentFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState == null){
currentFragment = new MapsFragment();
changeFragment(currentFragment);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu,menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()){
case R.id.menu_bookmarkList:
currentFragment = new ListaFragment();
break;
case R.id.menu_mapa:
currentFragment = new MapsFragment();
break;
}
changeFragment(currentFragment);
return super.onOptionsItemSelected(item);
}
private void changeFragment(Fragment currentFragment) {
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,currentFragment).commit();
}
}
ListaFragment (the one that should display the Recycler):
public class ListaFragment extends Fragment implements MyAdapter.RecyclerItemClick{
private MyAdapter myAdapter;
private RecyclerView recycler;
FirebaseDatabase database;
DatabaseReference myRef;
public ListaFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); }
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_lista, container, false);
database = FirebaseDatabase.getInstance();
myRef = database.getReference("Marcador");
recycler = (RecyclerView) v.findViewById(R.id.recyler);
recycler.setLayoutManager(new LinearLayoutManager(getContext()));
FirebaseRecyclerOptions<Marcador> options = new FirebaseRecyclerOptions.Builder<Marcador>()
.setQuery(myRef, Marcador.class).build();
myAdapter = new MyAdapter(options,this);
recycler.setAdapter(myAdapter);
return v;
}
#Override
public void onStart(){
super.onStart();
myAdapter.startListening();
}
#Override
public void onStop() {
super.onStop();
myAdapter.stopListening();
}
#Override
public void itemClick(Marcador marcador) {
}
}
And the adapter i've done (im skipping the xml's since they are quite simple):
public class MyAdapter extends FirebaseRecyclerAdapter<Marcador,MyAdapter.MarcadorHolder> {
private Context context;
private RecyclerItemClick itemClick;
public MyAdapter(#NonNull FirebaseRecyclerOptions<Marcador> options, RecyclerItemClick itemClick) {
super(options);
this.itemClick = itemClick;
}
#Override
protected void onBindViewHolder(#NonNull final MarcadorHolder holder, int position, #NonNull Marcador model) {
final Marcador marcador = getItem(position);
holder.textViewNom.setText(model.getNom());
holder.textViewLatitude.setText(String.valueOf(model.getLatitude()));
holder.textViewLongitude.setText(model.getLongitude());
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
itemClick.itemClick(marcador);
}
});
}
#NonNull
#Override
public MarcadorHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recylcer_view_item,parent,false);
context = parent.getContext();
return new MarcadorHolder(v);
}
public class MarcadorHolder extends RecyclerView.ViewHolder{
TextView textViewNom;
TextView textViewLatitude;
TextView textViewLongitude;
public MarcadorHolder(#NonNull View itemView) {
super(itemView);
textViewNom = itemView.findViewById(R.id.textViewNom);
textViewLatitude = itemView.findViewById(R.id.textViewLatitud);
textViewLongitude = itemView.findViewById(R.id.textViewLongitut);
}
}
public interface RecyclerItemClick {
void itemClick(Marcador marcador);
}
}
I hope someone can give me an answer of why this is happening, since i've checked other options and i can't really get through it.
XML's attached of fragment:
<?xml version="1.0" encoding="utf-8"?>
<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=".fragments.ListaFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyler"
android:layout_width="match_parent"
android:layout_height="545dp" />
</LinearLayout>
XML attached of item of recycler view:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="60dp" android:background="#CDCDCD" android:layout_marginBottom="5dp"
>
<TextView
android:id="#+id/textViewNom"
android:layout_width="410dp"
android:layout_height="55dp"
android:layout_marginLeft="15dp"
android:layout_marginTop="5dp"
android:text="Title"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="#+id/textViewLatitud"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="35dp"
android:text="latitud" />
<TextView
android:id="#+id/textViewLongitut"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="125dp"
android:layout_marginTop="35dp"
android:text="longitut" />
<ImageView
android:id="#+id/imageView"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_marginLeft="265dp"
android:layout_marginTop="10dp"
/>
</RelativeLayout>
Do use below lines
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recycler.setLayoutManager(layoutManager);
recycler.setAdapter(adapter);
According to Fragment API Reference:
It is recommended to only inflate the layout in this method and move logic that operates on the returned View to onViewCreated(View, Bundle).
So in onCreateView you should only inflate your layout hence the View v = inflater.inflate(R.layout.fragment_lista, container, false); you used and move logic of your code to onViewCreated and the reason you facing E/RecyclerView: No adapter attached; is because you're trying to setting adapter for your recyclerview before even creation/inflation of the view which is failing to complete therefore after the creation/inflation is done there's no adapter attached to recyclerview.
It is generally recommended to not initialize the component using findViewById in onViewCreated() as per the The Google Developer Documents.
You should inflate your layout in onCreateView but shouldn't
initialize other views using findViewById in onCreateView.
Because in this method not every View is not Properly initialized. So, prefer to use onViewCreated() to get your view's Id and do setUp the UI.

Sending data from Fragment to Fragment using Interface, by setting Fragment Layouts dynamically. Saved instance of Fragment NOT working

I am sending data from one fragment to another, using interface. This works absolutely fine. Now I am trying to use onSaveInstanceState() to save the value in
a Fragment and retrieving in onCreate(). however i'm getting null in Bundle of onCreate(). P.S. everything works fine when i set the Fragment directly into the activity layout in xml. but when i set the fragment through java code into the activity, the onSaveInstanceState is failing. It is not saving the last known value. Please help. Pasting the code below. You can try it out to know the exact issue.
Fragment_A :
public class Fragment_A extends Fragment implements View.OnClickListener {
Button btnAdd;
int counter = 0;
Communicator comm;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState != null){
counter = savedInstanceState.getInt("counter", 0);
}
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_a, container, false);
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
comm = (Communicator) getActivity();
btnAdd = (Button) getActivity().findViewById(R.id.btnAdd);
btnAdd.setOnClickListener(this);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("counter", counter);
}
#Override
public void onClick(View v) {
counter++;
comm.sendData(counter);
}
}
interface Communicator{
void sendData(int i);
}
=====
Activity :
public class Activity_InterComm extends Activity implements Communicator{
FragmentManager manager;
FragmentTransaction transaction;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_intercomm);
Fragment_A fragment_1 = new Fragment_A();
Fragment_B fragment_2 = new Fragment_B();
manager = getFragmentManager();
transaction = manager.beginTransaction();
transaction.add(R.id.frag_a, fragment_1, "Frag1");
transaction.add(R.id.frag_b, fragment_2, "Frag2");
transaction.commit();
}
#Override
public void sendData(int i) {
manager = getFragmentManager();
Fragment_B fragment_b = (Fragment_B) manager.findFragmentById(R.id.frag_b);
fragment_b.setData("Button has been clicked " + i + " times");
}
}
=====
Fragment_B :
public class Fragment_B extends Fragment {
TextView txtMessage;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_b, container, false);
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
txtMessage = (TextView) getActivity().findViewById(R.id.txtMessage);
}
public void setData(String message){
txtMessage.setText(message);
}
}
activity_intercomm.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">
<!--<fragment
android:id="#+id/frag_a"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
android:layout_weight="1"
android:name="android.learning.com.fragintercomm.Fragment_A"/>
<fragment
android:id="#+id/frag_b"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:name="android.learning.com.fragintercomm.Fragment_B"/>-->
<LinearLayout
android:id="#+id/frag_a"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
android:layout_weight="1"
android:background="#color/colorAccent"
android:orientation="vertical"/>
<LinearLayout
android:id="#+id/frag_b"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
android:layout_weight="1"
android:background="#color/colorPrimaryDark"
android:orientation="vertical"/>
</LinearLayout>
the soluion I found was using getView() instead of getActivity() while initializing the button in Fragment_A. use btnAdd = (Button) getView().findViewById(R.id.btnAdd); instead of btnAdd = (Button) getActivity().findViewById(R.id.btnAdd);.
However, you can't use getView() in Fragment_B. The Textview freezes and, onSaveInstanceStat() doesn't work. Please suggest a solution.

Android ListView not being displayed/refreshing and getView not working properly

Objective: I have one activity and within two fragments. The ideia is to communicate both fragments, selecting one category and updating its item list.On activity load, a category is settle by default.
Problem: On activity load, ItemsFragment doesn't display/update itemsLisView. Also ItemAdapter's getView Method is being called only twice, even though the list has five items.
Facts:
Items List is being correctly fulfilled before being passed to adapter
Breakpointing the code, not sure why or if it matters, ItemsFragment is being rendered/called before CategoriesFragments. I found this weird since CategoriesFragments is positioned above ItemsFragment. This is the reason why I wrote if (getArguments() != null). Before I was getting Null Pointer Exception.
Research: There is a lot of questions regarding both subjects.
Android ListView not refreshing after notifyDataSetChanged with data as Map
ListFragment Not Rendering and getView() in Adapter Not Being Called
Android getView() not being called properly?
BaseAdapter notifyDatasetChanged() called but getView() is never called
Those are some link examples (of thousands) I went through, trying to find a solution for my case. Unfortunately none of them worked.
I've been with this problem for almost 5 days now and I guess I missing some Android concept here. It's not something related with Java Programming, or at least I hope so.
Questions:
Why ItemsFragment is not displaying Items List on activity load? (This probably answers updating problem)
Why ItemAdapter just calls getView only twice, even that Items List is correctly given to him with more items?
For any further needed information, please don't mind to ask.
Thanks in advance for any help.
EDIT - MCVE:
activity_triple_list:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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:fitsSystemWindows="true"
tools:context=".controller.activities.Lists">
<include layout="#layout/content_triple_list" />
</android.support.design.widget.CoordinatorLayout>
content_triple_list:
<?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">
<android.support.v4.view.ViewPager
android:id="#+id/listsPager"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.PagerTabStrip
android:id="#+id/pager_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:paddingBottom="4dp"
android:paddingTop="4dp" />
</android.support.v4.view.ViewPager>
</LinearLayout>
fragment_categories:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/categoriesFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TableLayout
android:id="#+id/categoriesTable"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:stretchColumns="4">
<TableRow
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:layout_marginTop="5dp"
android:gravity="center_horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_column="0"
android:layout_marginLeft="13dp"
android:layout_marginRight="13dp"
android:orientation="vertical">
<TextView
android:id="#+id/category1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="CATEGORY 1"
android:textAlignment="center"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
</TableRow>
</TableLayout>
</LinearLayout>
fragment_items:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#id/itemsFragment"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<ListView
android:id="#+id/itemsListView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:divider="#null" />
</LinearLayout>
content_row_choose_item:
<?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:background="#drawable/custom_row"
android:orientation="vertical"
android:paddingBottom="5dp"
android:paddingLeft="15dp"
android:paddingTop="5dp">
<TextView
android:id="#+id/itemName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TEST"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
custom_row:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:background="#2fff00">
<item android:drawable="#color/colorPrimary" android:state_pressed="true" /> <!-- pressed -->
</selector>
ApplicationUtils:
public final class ApplicationUtils extends Application {
private static Context context;
public void onCreate() {
super.onCreate();
ApplicationUtils.context = getApplicationContext();
}
public static Context getContext() {
return ApplicationUtils.context;
}
public static String getJavaPackageName() {
return ApplicationUtils.context.getPackageName();
}
public static Resources getAppResources() {
return getContext().getResources();
}
public static String getResouceName(Integer id) {
return getAppResources().getResourceName(id);
}
public static String getResourceString(Integer id) {
return getAppResources().getString(id);
}
public static int getResourceId(String variableName, String resourceName, String packageName) {
try {
return getContext().getResources().getIdentifier(variableName, resourceName, packageName);
} catch (Exception e) {
e.printStackTrace();
return -1;
}
}
}
Lists:
public class Lists extends AppCompatActivity implements CategoriesFragment.OnHeadlineSelectedListener {
private String listName;
public void onItemSelected(String currentCategory) {
ItemsFragment itemsCallBackFragment = (ItemsFragment) getSupportFragmentManager().findFragmentById(R.id.itemsFragment);
if (itemsCallBackFragment != null) {
itemsCallBackFragment.updateArticleView(currentCategory);
} else {
ItemsFragment itemFragment = new ItemsFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.itemsFragment, itemFragment);
transaction.addToBackStack(null);
transaction.commit();
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_triple_list);
setListsAdapter();
}
private void setListsAdapter() {
ViewPager tripleListViewPager = (ViewPager) findViewById(R.id.listsPager);
FragmentPagerAdapter tripleListFragmentAdapt = new ListsPagerAdapter(getSupportFragmentManager(), "LIST NAME", ApplicationUtils.getContext());
tripleListViewPager.setAdapter(tripleListFragmentAdapt);
}
}
ListsPagerAdapter:
public class ListsPagerAdapter extends FragmentPagerAdapter {
private static int NUM_TABS = 1;
private String listName;
private Context listsContext;
public ListsPagerAdapter(FragmentManager fm, String newListName, Context listsContext) {
super(fm);
setListName(newListName);
setListsContext(listsContext);
}
// Returns total number of pages
#Override
public int getCount() {
return NUM_TABS;
}
// Returns the fragment to display for that page
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return NewItemPagerFragment.newInstance();
default:
return null;
}
}
// Returns the page title for the top indicator
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return listsContext.getResources().getString(R.string.lists_tab_newItem, getListName());
default:
return "$value";
}
}
public String getListName() {
return listName;
}
public void setListName(String listName) {
this.listName = listName;
}
public Context getListsContext() {
return listsContext;
}
public void setListsContext(Context listsContext) {
this.listsContext = listsContext;
}
}
CategoriesFragment:
public class CategoriesFragment extends Fragment {
private List<Integer> categoryIds = new ArrayList<>();
private String currentCategory;
private View categoriesView;
OnHeadlineSelectedListener itemCallback;
public interface OnHeadlineSelectedListener {
void onItemSelected(String categoryName);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
itemCallback = (OnHeadlineSelectedListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
categoriesView = inflater.inflate(R.layout.fragment_categories, container, false);
categoriesView.setId(R.id.categoriesFragment);
return categoriesView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setCategoriesListener();
setDefaultSelectedCategory();
}
private void setCategoriesListener() {
categoryIds.add(R.id.category1);
for (Integer id : categoryIds) {
setListener(id);
}
}
private void setListener(final int categoryId) {
final ImageView categoryImg = (ImageView) categoriesView.findViewById(categoryId);
categoryImg.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
removePreviousSelectedCategory(categoryImg);
selectCategory(categoryId, categoryImg);
loadItemList();
}
});
}
private void removePreviousSelectedCategory(ImageView categoryImg) {
for (Integer id : categoryIds) {
final ImageView imgView = (ImageView) categoriesView.findViewById(id);
if (imgView.getTag() != null) {
String previousSelectedCategory = (String) imgView.getTag();
if (StringUtils.contains(previousSelectedCategory, Application.SELECTED)) {
previousSelectedCategory = previousSelectedCategory.replace(Application.SELECTED, "");
categoryImg.setTag(previousSelectedCategory);
Integer previousSelectedCategoryId = ApplicationUtils.getResourceId(previousSelectedCategory, Application.RES_DRAWABLE, ApplicationUtils.getJavaPackageName());
imgView.setImageResource(previousSelectedCategoryId);
}
}
}
}
private void selectCategory(int categoryId, ImageView categoryImg) {
String newSelectedCategory = ApplicationUtils.getResouceName(categoryId) + Application.SELECTED;
setCurrentCategory(newSelectedCategory);
newSelectedCategory = newSelectedCategory.replace(Application.META_INFO_ID, "");
categoryImg.setTag(newSelectedCategory);
Integer currentCategoryId = ApplicationUtils.getResourceId(newSelectedCategory, Application.RES_DRAWABLE, ApplicationUtils.getJavaPackageName());
categoryImg.setImageResource(currentCategoryId);
}
public String getCurrentCategory() {
return currentCategory;
}
public void setCurrentCategory(String currentCategory) {
this.currentCategory = currentCategory;
}
private void loadItemList() {
itemCallback.onItemSelected(getCurrentCategory());
}
private void setDefaultSelectedCategory() {
Integer categoryId = R.id.category1;
final ImageView categoryImg = (ImageView) categoriesView.findViewById(categoryId);
selectCategory(categoryId, categoryImg);
loadItemList();
}
}
ItemsFragment:
public class ItemsFragment extends Fragment {
private View itemsFragmentView;
private ListView itemsListView;
private ItemAdapter itemsListAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
itemsFragmentView = inflater.inflate(R.layout.fragment_items, container, false);
itemsListView = (ListView) itemsFragmentView.findViewById(R.id.itemsListView);
setItemsListAdapter();
return itemsFragmentView;
}
public void updateArticleView(String categoryName) {
getSelectedCategoryList();
}
private void getSelectedCategoryList() {
List<String> testList = new ArrayList<>();
testList.add("TEST ITEM");
itemsListAdapter.update(testList);
}
private void setItemsListAdapter() {
itemsListAdapter = new ItemAdapter(getActivity(), R.layout.fragment_items, new ArrayList<String>());
itemsListView.setAdapter(itemsListAdapter);
}
}
ItemAdapter:
public class ItemAdapter extends ArrayAdapter<String> {
private List<String> items = new ArrayList<>();
private LayoutInflater rowInflater;
public ItemAdapter(Context context, int resource, List<String> itemsList) {
super(context, resource, itemsList);
this.items = itemsList;
this.rowInflater = LayoutInflater.from(context);
}
private class ItemHolder {
TextView itemNameView;
}
#Override
public View getView(int position, View rowView, ViewGroup parent) {
ItemHolder itemHolder;
if (rowView == null) {
rowView = rowInflater.inflate(R.layout.content_row_choose_item, parent, false);
itemHolder = new ItemHolder();
itemHolder.itemNameView = (TextView) rowView.findViewById(R.id.itemName);
rowView.setTag(itemHolder);
} else {
itemHolder = (ItemHolder) rowView.getTag();
}
String itemName = getItem(position);
if (StringUtils.isNotEmpty(itemName) && itemHolder.itemNameView != null) {
itemHolder.itemNameView.setText(itemName);
System.out.println("ITEM NAME ### " + itemHolder.itemNameView.getText());
}
return rowView;
}
public void update(List<String> items) {
this.items.clear();
this.items.addAll(items);
this.notifyDataSetChanged();
}
public List<String> getItems() {
return items;
}
}
Random stab...
If using a ListFragment, then fragment_items.xml needs a ListView with android:id="#android:id/list".
You are even trying to load the ListView with that id.
itemsFragmentView = inflater.inflate(R.layout.fragment_items, container, false);
itemsListView = (ListView) itemsFragmentView.findViewById(android.R.id.list);
However, you have android:id="#+id/itemsListView"
fragment_items:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#id/itemsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:id="#+id/itemsListView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:divider="#null" />
</LinearLayout>
If using a ListFragment, you do not need to implement your own XML layout file. That being said, onCreateView doesn't need implemented. Use onActivityCreated instead.
#Override
public void onActivityCreated(Bundle savedInstanceState) {
itemsListView = getListView();
itemsListAdapter = new ItemAdapter(getActivity());
setListAdapter(itemsListAdapter);
super.onActivityCreated(savedInstanceState);
}
Now, if you want to add items to the adapter, I generally recommend you just use an ArrayAdapter<String>. This will provide you an add(String object) method that will both add to the underlying list and notify for updates.
Regarding the getView problems, can't really say. I don't really know why you need ItemAdapter since the default ArrayAdapter<String> will work assuming you pass it a layout with android:id="#android:id/text1, for example using android.R.layout.simple_list_item_1 in place of R.layout.content_row_choose_item
So, that would look something like.
#Override
public void onActivityCreated(Bundle savedInstanceState) {
itemsListView = getListView();
ArrayAdapter<String> itemsListAdapter = new ArrayAdapter<String>(
getActivity(), android.R.layout.simple_list_item_1);
setListAdapter(itemsListAdapter);
adapter.add("Hello");
adapter.add("World");
super.onActivityCreated(savedInstanceState);
}
The given MCVE was working fine. After 7 days debugging and uncommenting code I found the answer to my problem.
My list was correctly being updated, but wasn't being rendered because of a second ItemsFragemnt being initialized on Lists Activity load. This second fragment was overlaying the first one and that's why I wasn't seing my list being displayed/refreshed.
Lists:
First created fragment:
private void setListsAdapter() {
ViewPager tripleListViewPager = (ViewPager) findViewById(R.id.listsPager);
FragmentPagerAdapter tripleListFragmentAdapt = new ListsPagerAdapter(getSupportFragmentManager(), "LIST NAME", ApplicationUtils.getContext());
tripleListViewPager.setAdapter(tripleListFragmentAdapt); }
Inside ListsPagerAdapter there was a call to populate my ViewPager (NewItemPagerFragment) with CategoriesFragment and ItemsFragment.
Second created fragment - Overlay:
ItemsFragment itemFragment = new ItemsFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.itemsFragment, itemFragment);
transaction.addToBackStack(null);
transaction.commit();
I tried to breakpoint my code, mentioned before in Facts at question scope, but sometimes this is really painful. I came with the idea to use Log.d(TAG_KEY, TAG_NAME); and that was when I found out the answer: I realized that ItemsFragment was being called twice on Activity load.
I searched for new ItemsFragment in my project and saw that inside my NewItemPagerFragment onCreate, ItemsFragment was being called. Also before CategoriesFragment- just like this:
NewItemPagerFragment:
Fragment itemsFragment = new ItemsFragment();
FragmentManager fm1 = getChildFragmentManager();
FragmentTransaction ft1 = fm1.beginTransaction();
ft1.replace(R.id.itemsFragment, itemsFragment);
ft1.commit();
Fragment categoriesFragment = new CategoriesFragment();
FragmentManager fm = getChildFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.categoriesFragment, categoriesFragment);
ft.commit();
which explains Facts 2.. After commenting/remove this call on NewItemPagerFragment the problem was gone.
Bottom line is: Make sure your list is correctly being fulfilled/updated with things like notifyDataSetChanged as cricket_007 mentioned in his answer and other thousand posts I read on internet. Not less, make sure you are not overlaying the fragment calling its initialization twice.

Android. Get listview item onClick inner button to new child fragment

I have a Fragment with a ListView of row items. A row consist in just am ImageView
fragment_1.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/fragment_1"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Fragment1">
<ListView
android:id="#+id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:divider="#b5b5b5"
android:dividerHeight="1dp"
android:listSelector="#drawable/list_selector"/>
</RelativeLayout>
row.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/row"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#drawable/list_selector"
android:orientation="horizontal"
android:padding="5dip">
<!-- Rightend Play Button. -->
<ImageView
android:id="#+id/play_button"
android:layout_width="50dip"
android:layout_height="50dip"
android:src="#drawable/ic_play"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"/>
</RelativeLayout>
I have a ResourceCursorAdapter to fill this ListView. I just assign to each ImageView a click listener. It plays a record saved in the provided path.
private abstract class MyAdapter extends ResourceCursorAadpter {
#Override
public void bindView(View view, Context context, final Cursor cursor) {
// Play button.
ImageView playButton = (ImageView) view.findViewById(R.id.play_button);
playButton.setOnClickListener(new View.OnClickListener() {
String record = cursor.getString(cursor.getColumnIndex(DbAdapter.RECORD));
public void onClick(View v) {
// Play record in path [record]. Not the problem.
}
});
}
}
Now I want to open a new fragment Fragment_2 in a row click. This fragment has the same play_button, and has to play the same record.
fragment_2.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/fragment_audio"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Fragment2">
<!-- Play Button. -->
<ImageView
android:id="#+id/play_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/ic_play"/>
</RelativeLayout>
How can I manage that this button plays the same record than in Fragment_1? I think I could be able if I have a hidden TextView with the path of the record, but sure you have a smarter solution.
In my onCreateView of Fragment_1.
mList = (ListView) root.findViewById(R.id.list);
// Click event for single row.
mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// TODO: Go to fragment_2 and has the same view [view.findViewById(R.id.play_button)]
}
});
Communication between fragments should be done through the associated Activity. You can use interface for this. it Will serve as contract and bridge between them.
Let's have the following components:
An activity hosts fragments and allow fragments communication
FragmentA first fragment which will send data
FragmentB second fragment which will receive datas from FragmentA
FragmentA's implementation is:
public class FragmentA extends Fragment {
DataPassListener mCallback;
public interface DataPassListener{
public void passData(String data);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Make sure that container activity implement the callback interface
try {
mCallback = (DataPassListener)activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement DataPassListener");
}
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Suppose that when a button clicked second FragmentB will be inflated
// some data on FragmentA will pass FragmentB
// Button passDataButton = (Button).........
passDataButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (view.getId() == R.id.passDataButton) {
mCallback.passData("Text to pass FragmentB");
}
}
});
}
}
MainActivity implementation is:
public class MainActivity extends ActionBarActivity implements DataPassListener{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (findViewById(R.id.container) != null) {
if (savedInstanceState != null) {
return;
}
getFragmentManager().beginTransaction()
.add(R.id.container, new FragmentA()).commit();
}
}
#Override
public void passData(String data) {
FragmentB fragmentB = new FragmentB ();
Bundle args = new Bundle();
args.putString(FragmentB.DATA_RECEIVE, data);
fragmentB .setArguments(args);
getFragmentManager().beginTransaction()
.replace(R.id.container, fragmentB )
.commit();
}
}
FragmentB implementation is:
public class FragmentB extends Fragment{
final static String DATA_RECEIVE = "data_receive";
TextView showReceivedData;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_B, container, false);
showReceivedData = (TextView) view.findViewById(R.id.showReceivedData);
}
#Override
public void onStart() {
super.onStart();
Bundle args = getArguments();
if (args != null) {
showReceivedData.setText(args.getString(DATA_RECEIVE));
}
}
https://developer.android.com/training/basics/fragments/communicating.html
This document has given information in detail, you can go through it.
In your situation, you can apply this way to communicate between two fragments.

ViewPage Fragment disappear when reload again

The following is layout
test.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="Click"
android:id="#+id/button"
/>
<!-- Framelayout to display Fragments -->
<FrameLayout
android:id="#+id/frame_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
test_detail.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Hello"
android:id="#+id/textView" android:layout_gravity="center_horizontal"/>
</LinearLayout>
common_view_pager_layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="#+id/MainViewerPage"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<android.support.v4.view.PagerTabStrip
android:id="#+id/TitlePageTabStrip"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.v4.view.ViewPager>
</LinearLayout>
The following is my Activity code
public class TestFragmentActivity extends FragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.test);
Button button = (Button) this.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.frame_container, new HomeFragment()).commit();
}
});
}
public class HomeFragment extends Fragment {
private PagerAdapter _adapter;
public HomeFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.common_view_pager_layout, container, false);
this._adapter = new PagerAdapter(TestFragmentActivity.this);
this._adapter.add(new DetailFragment());
ViewPager pager = (ViewPager) view.findViewById(R.id.MainViewerPage);
pager.setAdapter(this._adapter);
return view;
}
}
public class DetailFragment extends Fragment {
public DetailFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.test_detail, container, false);
return rootView;
}
}
public class PagerAdapter extends FragmentPagerAdapter {
private ArrayList<Fragment> _fragments;
public PagerAdapter(FragmentActivity activity) {
super(activity.getSupportFragmentManager());
this._fragments = new ArrayList<Fragment>();
}
public void add(Fragment fragment) {
this._fragments.add(fragment);
}
#Override
public Fragment getItem(int position) {
return this._fragments.get(position);
}
#Override
public CharSequence getPageTitle(int position) {
return "Test";
}
#Override
public int getCount() {
return 1;
}
}
}
When I click a button the fragment in ViewPager was display
But when I click the button again the fragment disappear
Please help me.
try using this:
in the constructor of your adapter change this :
public class MyPagerAdapter extends FragmentPagerAdapter {
private ArrayList<Fragment> _fragments;
public MyPagerAdapter(FragmentManager activity) {
super(activity);
this._fragments = new ArrayList<Fragment>();
}
public void add(Fragment fragment) {
this._fragments.add(fragment);
}
#Override
public Fragment getItem(int position) {
return this._fragments.get(position);
}
#Override
public CharSequence getPageTitle(int position) {
return "Test";
}
#Override
public int getCount() {
return 1;
}
}
and when you want to create the adapter send getChildFragmentManager() to its constructor
public class HomeFragment extends Fragment {
private MyPagerAdapter _adapter;
public HomeFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.common_view_pager_layout, container, false);
ViewPager pager = (ViewPager) view.findViewById(R.id.MainViewerPage);
this._adapter = new MyPagerAdapter(getChildFragmentManager());
this._adapter.add(new DetailFragment());
pager.setAdapter(this._adapter);
return view;
}
}
I test it and it works, any problem comment it.
Good Luck!
In your Fragment class, in the onCreate() method, you have to call setRetainInstance(true) like this:
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
This tells the FragmentManager to keep your fragments on reloads.
In addition, check your manifest file doesn't have any
android:screenOrientation
config changes where you are basically saying you will handle reloads due to config changes yourself.
In PagerFragment 's onResume() add this:
#Override
public void onResume() {
super.onResume();
for (Fragment fragment : getFragmentManager().getFragments()) {
if (fragment instanceof Tab1Fragment || fragment instanceof Tab2Fragment) {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.detach(fragment);
ft.attach(fragment);
ft.commit();
}
}
}
Try to add this lines in your code:
public int getItemPosition(Object object) {
return POSITION_NONE;
}
and Add the below lines
pager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
... anything you may need to do to handle pager state ...
adapter.notifyDataSetChanged(); //this line will force all pages to be loaded fresh when changing between fragments
}
Under
pager.setAdapter(adapter);
Hope this may help you!
Ensure you pass the adapter instance the child fragment manager, not the fragment manager.
Like this:
this._adapter = new PagerAdapter(getChildFragmentManager());

Categories

Resources