I have 6 fragments, all with the same layout and functionality, the data [A,B,C,D,...] for each fragment is different. When the first two fragments are created I view B,B then C then D...etc. Swiping back from C I get the correct data viewed C, B, A.
I can't see what I am doing wrong. Here is a bit of code :
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
private final Bundle fragmentBundle;
public ScreenSlidePagerAdapter(FragmentManager fm, Bundle data) {
super(fm);
fragmentBundle = data;
}
#Override
public Fragment getItem(int position) {
// creating fragment and passing int position for array and array
return SoundFrag.newInstance(position, fragmentBundle);
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
and the fragment class :
public class SoundFrag extends Fragment{
// fragment Id Postion
private int fragPosition;
private static final String KEY_POSITION="fragIntIdP";
static SoundFrag newInstance(int position, Bundle arrayIntS) {
SoundFrag frag=new SoundFrag();
arrayIntS.putInt(KEY_POSITION, position);
frag.setArguments(arrayIntS);
return(frag);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// retrive data from bundle passed by activity
Bundle argsIn = getArguments();
SoundArrayParcel arrayToReceive = argsIn.getParcelable("dataResId");
final int[][] arrayResId = arrayToReceive.getInt();
fragPosition = argsIn.getInt("fragIntIdP");
// inflate the fragment
final ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.sounds_frag, container, false);
.....
I believe the problem to be here. The value of fragPosition is always 1 for the first two fragments, instead of 0 and then 1. Why?
Thanks for any help !
For what it's worth...
I changed the code so that the array was no longer passed to the fragment via Bundle. I now only pass the position value of the fragment (from the getItem(int position) method) and the array is 'constructed' within the fragment once it inflates.
The condition I was noticing before (fragposition being 1 for both first and second fragments) no longer happens and the apps displays the information correctly.
Why ? Not sure, but it works. I will however continue to investigate as I don't like the solution I found.
Related
I dont know if my title is correct but I hope you can get my question with the help of some pictures. So I have a project the will display a fragment containing book info such us Title, Author, Publisher etc, at the same time I want to display comments about the book I mean once it loads. It also loads a recyclerview containing the comments about the books like in google play store where you may see the comments of different users. How can I achieve this? I do know how to create a recyclerview. here is the image for more clarity.
For RecyclerView you need to add
compile 'com.android.support:recyclerview-v7:24.2.1'
in your dependencies. Inside module level build.gradle file. After adding the compilation file , sync your project.
Then inside your layout add the recyclerView. Like this
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_funds"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:listitem="#layout/layout_funds_item" />
here the attribute "listitem" will show how your recyclerview will be seen when items will be inflated.
Now in your activity or fragment initialize the recyclerview.
private RecyclerView mRecyclerView;
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_funds);
After that you need to set the layoutManger for your recyclerview. A LayoutManager is responsible for measuring and positioning item views within a RecyclerView as well as determining the policy for when to recycle item views that are no longer visible to the user. By changing the LayoutManager a RecyclerView can be used to implement a standard vertically scrolling list, a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock layout managers are provided for general use.
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
After that set your adapter to the recyclerview.
mRecyclerView.setAdapter(new FundsAdapter(mContext));
Here is the code sample how this FundsAdapter looks like.
public class FundsAdapter extends RecyclerView.Adapter<FundsAdapter.ItemHolder> {
private Context mContext;
public FundsAdapter(Context context) {
mContext = context;
}
#Override
public ItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.layout_funds_item, parent, false);
return new FundsAdapter.ItemHolder(view);
}
#Override
public void onBindViewHolder(ItemHolder holder, int position) {
}
#Override
public int getItemCount() {
return 10;
}
public class ItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView vh_Title;
private TextView vh_FundName;
public ItemHolder(View itemView) {
super(itemView);
vh_Title = (TextView) itemView.findViewById(R.id.txt_fund_bank);
vh_FundName = (TextView) itemView.findViewById(R.id.txt_fund_name);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
mContext.startActivity(new Intent(mContext, FundsDetailActivity.class));
}
}
}
Hope you got your solution. :)
I have a WearableActivity as my main activity and I want to use fragments for different layouts. The trouble is when I try to get a reference to the main activity using getActivity() in the fragment, Android Studio says that I cannot cast from FragmentActivity to my MainWearActivity(which extends WearableActivity).
My MainWearActivity class:
public class MainWearActivity extends WearableActivity implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener{
private WatchViewStub mContainerView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_wear);
mContainerView = (WatchViewStub) findViewById(R.id.mainContainer);
mContainerView.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
#Override
public void onLayoutInflated(WatchViewStub watchViewStub) {
}
});
}
#Override
public void onConnected(Bundle bundle) {
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
}
#Override
public void onEnterAmbient(Bundle ambientDetails) {
super.onEnterAmbient(ambientDetails);
}
#Override
public void onUpdateAmbient() {
super.onUpdateAmbient();
}
#Override
public void onExitAmbient() {
super.onExitAmbient();
}
}
My fragment class:
public class NavigationFragment extends Fragment {
View view;
MainWearActivity mMainWearActivity;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.navigation_fragment, container, false);
mMainWearActivity = (MainWearActivity) getActivity(); // ERROR HERE
return view;
}
}
When I try to the result of cast getActivity() to my main activity it won't allow me. Can anyone help me to get around this? I need a reference to my main activity so that I can use it as a context. Thanks
EDIT
String's answer below solved the problem. An additional note I'd like to add however, is that if you are using the fragment pager adapter, you will have to add "compile 'com.android.support:support-v13:23.1.1'" to your build.gradle, and use the support v13 pager adapter which allows you to return an android.app.Fragment from GetItem().
Is your NavigationFragment an instance of android.support.v4.app.Fragment, by any chance? I'm guessing so, because that's the Fragment class that expects getActivity() to return a FragmentActivity.
If my guess is correct, what you need to do is find the line in your NavigationFragment.java file that reads:
import android.support.v4.app.Fragment;
and change it to
import android.app.Fragment;
You may need to change a few other imports and calls similarly, to remove reference to the support library and its classes. But there's no need to be using support fragments on Wear - all the devices are well new enough to have fragments natively.
You should implement the AmbientMode.AmbientCallbackProvider instead of using WearableActivity.
It is the new preferred method and it still gives you the onEnterAmbient(), onAmbientUpdate(), and onExitAmbient() but also lets you use Activity (or any sub classes... FragementActivity, etc.). It also allows you to support the Architecture components.
Official docs call out the details.
I need to implement search functionality inside my android app which uses toolbar, SlidingTabLayout and ViewPager that holds fragments. Inside each fragment there is a RecyclerView with list of items.
RecyclerView data is static defined in separate class (DataAccess.java) and those lists are updated and RecyclerView gets refreshed just by calling (without passing new data)
mRecyclerView.getAdapter().notifyDataSetChanged();
Is there any simple way to temporary filter RecyclerView without changing the data and after the user presses return button inside Toolbar to remove the filter and show inital list.
Before pressing Search icon inside toolbar menu:
So when the user is typing "Josip..." the filter will be active
and after he presses the X button in SearchView the user will get the same data as before without filter.
#Override
public boolean onQueryTextChange(String newText) {
// filter data (temporary remove all items from DataAccess.list that don't .startsWith(newText)
}
#Override
public boolean onQueryTextSubmit(String query)
// Doesn't help if I revert deleted items here
}
#Override
public boolean onQueryTextSubmit(String query){
((ItemAdapter) myRecList.getAdapter()).setFilter(query)
}
public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.ViewHolder> {
private List<String> visibleObjects;
private List<String> allObjects;
.....
public void flushFilter(){
visibleObjects=new ArrayList<>();
visibleObjects.addAll(allObjects);
notifyDataSetChanged();
}
public void setFilter(String queryText) {
visibleObjects = new ArrayList<>();
constraint = constraint.toString().toLowerCase();
for (String item: allObjects) {
if (item.toLowerCase().contains(queryText))
visibleObjects.add(item);
}
notifyDataSetChanged();
}
}
I wanted to add as comment but due to less reputation...I am answering post.
This method works fine if (item.toLowerCase().contains(queryText)) but what to do if match is not found in first iteration.then it will go in else part without looping throughout allObjects list...
for (RouteByATMList.Route_ATM item: Main_ATMItemList)
{
if (item.ATMNumber.contains(queryText)) {
visibleObjects.add(item);
}else {
Toast.makeText(mContext,"No search result found!",Toast.LENGTH_SHORT).show();
break;
}
}
I got the answer from my superior ,hope it helps.
public void setFilter(String queryText) {
visibleObjects = new ArrayList<>();
for (RouteByATMList.Route_ATM item: Main_ATMItemList)
{
if (item.ATMNumber.contains(queryText))
{
visibleObjects.add(item);
}
}
if(visibleObjects.size()==0){
Toast.makeText(mContext,"No search result found!",Toast.LENGTH_SHORT).show();
}
notifyDataSetChanged();
Log.e("dataset changed","dataset changed");
}
I don't oppose the given and accepted answer. There is a room for possible performance pitfalls. One should make use of Filterabe interface. Having this implemented will behave as ol'good ListView that did the filtering asynchronously. Then all you need is to write your own Filter and instantiate it in the overridden getFilter() method;
In my case I used Filter to sort an adapter of many (yeah, many many) items. It was junky sorting it on UI-thread.
So I had this abstract class
public abstract class BaseFilterableRecyclerViewAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> implements Filterable {
private Context mContext;
public BaseFilterableRecyclerViewAdapter(Context context) {
this.mContext = context;
}
public abstract void sort(SortingFilter.Sort sortingStrategy);
//...Other methods
}
And the class that inherit:
public class ItemAdapter extends BaseFilterableRecyclerViewAdapter<RecyclerView.ViewHolder>{
//... RecyclerView methods
#Override
public Filter getFilter() {
return new SortingFilter(mData) {
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.values != null) {
int last = mData.size();
mData = (List<Product>) results.values;
notifyDataSetChanged();
}
}
};
}
#Override
public void sort(SortingFilter.Sort sortingStrategy) {
getFilter().filter(sortingStrategy.toString());
}
}
I am creating an application which has an activity which contains viewpager and this viewpager acts as a container for six fragments. The activity also has a spinner which contains 8 items. The thing which I am trying to achieve is that when I select an item from a spinner then fragments inside viewpager should be destroyed and six new fragments should be populated.
I am using a FragmentStatePagerAdapter to achieve this.
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public ScreenSlidePagerAdapter(FragmentManager manager, ArrayList<Fragment> fmList) {
super(manager);
pagerFragmentList = fmList;
}
#Override
public Fragment getItem(int position) {
return pagerFragmentList.get(position);
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public int getCount() {
return pagerFragmentList.size();
}
}
This is how I load new data.
pagerFragmentList.clear();
pagerFragmentList = newFragmentList;
mPagerAdapter.notifyDataSetChanged();
mPager.setCurrentItem(0);
As I told that I have six fragments inside viewpager and 8 items inside spinner so that makes total of 48 fragment classes but thing is that I am only using six xml layout files for my view and I inflate these views inside all my fragments.
First time when fragments are loaded everything works fine but when I select an item from a spinner then the fragment shows me pre-entered values which I entered when first item of spinner was selected.
This is the snapshot of my app
Im not sure but you can try this way
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public ScreenSlidePagerAdapter(FragmentManager manager, ArrayList<Fragment> fmList) {
super(manager);
pagerFragmentList = fmList;
}
#Override
public Fragment getItem(int position) {
return pagerFragmentList.get(position);
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public int getCount() {
return pagerFragmentList.size();
}
public void mDatasetChanged(ArrayList<Fragment> mFmList){
pagerFragmentList = mFmList;
this.notifyDataSetChanged();
}
}
And then in your activity
pagerFragmentList.clear();
pagerFragmentList = newFragmentList;
mPagerAdapter.mDatasetChanged(pagerFragmentList);
mPager.setCurrentItem(0);
I'm trying to modify an old FragmentActivity hosting a view pager with 3 listview in a Fragment.
The Fragment class has code that is similar to an Activity. In fact, when converting an existing Android application to use fragments, I though it would have been sufficient to move code from the activity's callback methods into the respective callback methods of the fragment. But it didn't work. I get a NullPointerException at the line
mViewPager.setAdapter(mAppSectionsPagerAdapter);
here is the code:
public class FragmentAllEvents extends Fragment implements ActionBar.TabListener
{
AppSectionsPagerAdapter mAppSectionsPagerAdapter;
// JSON Node names
private static final String TAG_UID = "uid";
private static final String TAG_LOGO = "logo";
private static final String TAG_POKUID = "pokuid";
static ArrayList<HashMap<String, String>> userList;
ArrayList<HashMap<String, String>> userListTotal;
HashMap<String, String> userSelected;
EventsFunctions eventsFunctions;
UserFunctions userFunctions;
/**
* The {#link ViewPager} that will display the three primary sections of the app, one at a
* time.
*/
ViewPager mViewPager;
static ListView lv;
ActionBar actionBar;
//Context context = this;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
View v = inflater.inflate(R.layout.all_events_main, container, false);
Log.e("AllEventsFragmentActivity Report", "Entering AllEventsFragments");
// Create the adapter that will return a fragment for each of the three primary sections
// of the app.
//FragmentManager fm = getSupportFragmentManager();
mAppSectionsPagerAdapter = new AppSectionsPagerAdapter(getFragmentManager());
// Set up the action bar.
actionBar = getActivity().getActionBar();
// Specify that the Home/Up button should not be enabled, since there is no hierarchical
// parent.
actionBar.setHomeButtonEnabled(true);
// Specify that we will be displaying tabs in the action bar.
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Set up the ViewPager, attaching the adapter and setting up a listener for when the
// user swipes between sections.
mViewPager = (ViewPager) getActivity().findViewById(R.id.pager_main);
mViewPager.setAdapter(mAppSectionsPagerAdapter);
// Defining a listener for pageChange
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener()
{
[...]
});
[...]
}
In the previous version of this class I was extending FragmentAllEvents with a FragmentActivity instead of a Fragment.
It appears like, mViewPager is not populated and is NULL. If pager_main is in all_events_main layout, you need to use v.findViewById(R.id.pager_main)