Elements inside viewpagers` fragment - java

I have a problem. I have a viewpager with 3 fragments inside. In first fragment i have some ImageViews.
First of all how make that imageviews visible with timer? I used thise code below but i have error which looks like: variable 'mImageView' is accessed from within inner class, needs to be declared class.
mImageView.setVisibility(View.INVISIBLE);
mImageView.postDelayed(new Runnable() {
public void run() {
mImageView.setVisibility(View.VISIBLE);
}
}, 5000);
How can i solve this problem?
Second I tried to move that elements (ImageViews) by X values when user start scrolling from first fragment to next fragment. It works but when i go to last 3-d fragment app crash. So why it happen?!
MainActivity.java
pager.setPageTransformer(false, new ViewPager.PageTransformer() {
#Override
public void transformPage(View page, float position) {
// transformation here
final float normalizedPosition = Math.abs(Math.abs(position) - 1);
page.setAlpha(normalizedPosition);
int pageWidth = page.getWidth();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
page.setAlpha(0);
} else if (position <= 1) { // [-1,1]
ImageView mImageView = (ImageView)findViewById(R.id.imageView2);
mImageView.setTranslationX((float) (-(1 - position) * 1.7 * pageWidth));
mImageView.setVisibility(View.INVISIBLE);
mImageView.postDelayed(new Runnable() {
public void run() {
mImageView.setVisibility(View.VISIBLE);
}
}, 5000);
// The 0.5, 1.5 values you see here are what makes the view move in a different speed.
// The bigger the number, the faster the view will translate.
// The result float is preceded by a minus because the views travel in the opposite direction of the movement.
}
else{ // (1,+Infinity]
// This page is way off-screen to the right.
page.setAlpha(0);
}
}
});
Third: Is it possible to make move elements by circle when user scroll. Need any help!

For your first question, as I said in the comment, you need to make the mImageView variable final
final ImageView mImageView = (ImageView)findViewById(R.id.imageView2);
Then, the null pointer exception, is probably caused (as Blackbelt said), because you're using the activity's findViewById method, and probably the imageView you need is in the fragment view:
final ImageView mImageView = (ImageView) page.findViewById(R.id.imageView2);
And for your 3º question, please explain what you mean by "move by circle", then I'll update my post(if I can) with an answer.

MainActivity.java
// Initialize the ViewPager and set an adapter
ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
ViewPagerAdapter.java
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
private final int PAGES = 3;
private String[] titles={"News", "Organizations", "Map"};
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new TabFragment1();
case 1:
return new TabFragment2();
case 2:
return new TabFragment3();
default:
throw new IllegalArgumentException("The item position should be less or equal to:" + PAGES);
}
}
#Override
public CharSequence getPageTitle(int position) {
return titles[position];
}
#Override
public int getCount() {
return PAGES;
}
}
As you see for each page I have individual fragment documents. Here below one of them:
public class TabFragment1 extends Fragment{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_tab_1, container, false);
}
}
fragment_tab_1.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" >
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="#drawable/city"
android:id="#+id/imageView2"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>

Related

Can single Activity be used for multiple actions?

The cheat code app will have a list of games. And when each game is tapped, the cheat codes for that game will be displayed. My question is, do I have to create an activity for each game listed or is there a way to make it work in one activity.
No you don’t need to make individual activity for each game listed rather you can go for a RecyclerView and fragments combination to make it work in one activity.
here i have added a recyclerView for the game list and the when items of the game list will be clicked then the recylerview will be hidden and the frameLayout id(content) will be populated with the fragment and when the back btn is pressed while inside the fragment reverse will happen( hiding the fragment and showing the recyclerView).
though this whole approach can be far more simplified if all the game have similar type of data to show then you can,
in that case you can pass data to the fragment and make it work with only one fragment.
hope this answer the question.
**
XML code
**
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
**
Adapter code
**
public abstract class SettingsAdapter extends RecyclerView.Adapter<SettingsAdapter.MyViewHolder> {
private static final String TAG = "SettingsAdapter";
private LayoutInflater inflater;
private Context context;
private List<SettingData> data;
public SettingsAdapter(Context context , List<SettingData> data){
inflater = LayoutInflater.from(context);
this.data = data;
this.context = context;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.custom_settings_row, parent, false);
SettingsAdapter.MyViewHolder holder = new SettingsAdapter.MyViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
SettingData current = data.get(position);
holder.settinsName.setText(current.settingName);
holder.hostRelativeLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
OnButtonClicked(v, position);
}
});
}
#Override
public int getItemCount() {
return data.size();
}
protected abstract void OnButtonClicked(View v ,int position);
class MyViewHolder extends RecyclerView.ViewHolder {
private final TextView settinsName;
private final RelativeLayout hostRelativeLayout;
public MyViewHolder(View itemView) {
super(itemView);
settinsName = itemView.findViewById(R.id.settings_name);
hostRelativeLayout = itemView.findViewById(R.id.hostRelativeLayout);
}
}
}
**
android activity code inside onCreate
**
SettingsAdapter settingsAdapter = new SettingsAdapter(this, getFinalData()) {
#SuppressLint("RestrictedApi")
#Override
protected void OnButtonClicked(View v, int position) {
switch (position) {
case 0:
settingsContent.setVisibility(View.GONE);
fab.show();
configAppbarTittle(StudentSettingsActivity.this, settingNameArr[position]);
appBarLayout.setExpanded(false);
getSupportFragmentManager().beginTransaction().replace(R.id.content, new SavedPlacesFragment()).commit();
break;
case 1:
//similar
break;
case 2:
//similar
break;
case 3:
//similar
break;
default:
break;
}
}
};
settingsRecycleView.setAdapter(settingsAdapter);
settingsRecycleView.setLayoutManager(new LinearLayoutManager(this));
**
fragment code inside activity
**
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class SavedPlacesFragment extends Fragment {
private StudentSettingsActivity myMainActivity;
private RecyclerView savedPlacesRecycler;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
myMainActivity = (StudentSettingsActivity) getActivity();
}
#SuppressWarnings("unchecked")
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
//myMainActivity = (StudentSettingsActivity) getActivity();
View rootView = inflater.inflate(R.layout.stu_setting_saved_places_fragment, container, false);
return rootView;
}
private void flush(String msg) {
Toast.makeText(myMainActivity, msg, Toast.LENGTH_SHORT).show();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
myMainActivity.fab.hide();
startActivity(new Intent(getActivity(), StudentSettingsActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
}
Please understand the fact that i have copy pasted from my production
code here. Just to demonstrate the way. If you copy paste this code it
won't work. You have to read through the code to understand the way
it's been implemented and then write it in your own way
...You say that the cheat code app have list of games
You create one activity to show the list
....And if u tap on list to get new game
So you have to create fragment or activity to show the game by click on list
You can create a single Activity and have it hold multiple fragments.
Your Activity ( Lets say MainActivity ) will be responsible for displaying fragments.
At a high level, you need 2 fragments:
GameListFragment
for displaying Game List and game item click. You will need a recycler view to display list of games on this fragment.
GameCheatCodeDetailsFragment
for displaying Cheat Code.
Refer to SmartShows to get some code reference.

GridView scrollBar: erratic behaviour when dynamically adding images

I am trying to make some kind of image gallery where images are loaded in the background and are dynamically added to a gridView when they have finished loading. The image loading works quite well, but the gridView's scrolling behaviour won't work as expected if the images inside the gridView exceed the screen's height.
For testing purposes I am loading 15 dummy images, aligend in two columns. After all images are loaded it seems that the gridView's height fits its content height (8 images or rows on the left column) according to the scrollBar on the right. But if I try to scroll past the 4th row item to reach the bottom of the view (row 5/6/7/8), the scrollBar indicates that the gridView's height has changed and the bottom of the view is reached. Scrolling past the 4th line is not possible. If I scroll up again, the gridView seems to contain 8 rows again.
Left view: gridView seems to contain 15 images.
Right view: gridView suddenly seems to contain only 8 images
I have already tried using different approaches like the ExpandableHeightViewGrid mentioned here, but the scrolling behaviour was the same. I would choose using a gridView having multiple columns of images over a single row (like using a listView) because if I there are more than 15 images to load, scrolling to the bottom would be very annoying.
Here is my code:
photo_gallery.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">
<!-- This is basically a HorizontalScrollView where i add some buttons -->
<com.my.HorizontalButtonScrollList
android:id="#+id/horizontalButtonScrollList"
android:layout_width="match_parent"
android:layout_height="50dip">
</com.my.HorizontalButtonScrollList>
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<GridView
android:id="#+id/gridView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnWidth="100dp"
android:numColumns="2"
android:verticalSpacing="0dp"
android:horizontalSpacing="0dp"
android:stretchMode="columnWidth"
android:gravity="center"
android:scrollbars="vertical">
</GridView>
</android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>
PhotoGalleryActivity.java (I simplified the code for better readability)
public class PhotoGalleryActivity extends myBaseView {
private GridView gridView;
private PhotoGalleryImageAdapter imageAdapter;
private PhotoGalleryModel[] photoGalleryModels;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.photo_gallery);
gridView = (GridView) findViewById(R.id.gridView);
loadImages();
}
void loadImages() {
photoGalleryModels = PhotoGalleryModel.getFakeData();
imageAdapter = new PhotoGalleryImageAdapter(this, photoGalleryModels);
gridView.setAdapter(imageAdapter);
}
}
PhotoGalleryImageAdapter (also simplified)
public class PhotoGalleryImageAdapter extends BaseAdapter {
private Context mContext;
private PhotoGalleryModel[] photoGalleryModels;
public PhotoGalleryImageAdapter(Context c, PhotoGalleryModel[] models){
mContext = c;
photoGalleryModels = models;
}
#Override
public int getCount() { return photoGalleryModels.length; }
#Override
public Object getItem(int position) { return null; }
#Override
public long getItemId(int position) { return 0; }
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final ImageView imageView = new ImageView(mContext);
DownloadImageWithURL(photoGalleryModels[position].thumb_image_url, new MyHttpCallback() {
#Override
public void MyHttpCallback_OnSuccess(Object data, String responseString)
{
if(data instanceof Bitmap) {
imageView.setImageBitmap((Bitmap)data);
}
}
#Override
public void MyHttpCallback_OnError(String responseString, ErrorDataModel error)
{}
});
convertView = imageView;
return convertView;
}
}
I would be really glad if someone could help me out here and help me fix my gridView so that I can scroll through all of the loaded images as intended.
Well, it seems that I've solved the problem myself by ignoring it. After I skipped fixing the gridView because I did not know what to do anymore I implemented caching the images with an LruCache (like shown in the Android developer's training page) to save some memory. And suddenly the gridView's scrolling behaviour was fixed, too.
Here are my changes:
PhotoGalleryImageAdapter (now with caching)
public class PhotoGalleryImageAdapter extends BaseAdapter {
private Context mContext;
private PhotoGalleryModel[] photoGalleryModels;
private LruCache<String, Bitmap> mMemoryCache;
public PhotoGalleryImageAdapter(Context c, PhotoGalleryModel[] models){
mContext = c;
photoGalleryModels = models;
final int maxMemory = (int)(Runtime.getRuntime().maxMemory() / 1024);
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
#Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getByteCount() / 1024;
}
};
}
#Override
public int getCount() { return photoGalleryModels.length; }
#Override
public Object getItem(int position) { return null; }
#Override
public long getItemId(int position) { return 0; }
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final ImageView imageView = new ImageView(mContext);
final String imageKey = photoGalleryModels[position].thumb_image_url;
final Bitmap bitmapImage = mMemoryCache.get(imageKey);
if (bitmapImage != null) {
imageView.setImageBitmap(bitmapImage);
}
else {
DownloadImageWithURL(photoGalleryModels[position].thumb_image_url, new MyHttpCallback() {
#Override
public void MyHttpCallback_OnSuccess(Object data, String responseString) {
if (data instanceof Bitmap) {
mMemoryCache.put(imageKey, (Bitmap)data);
imageView.setImageBitmap((Bitmap)data);
}
}
#Override
public void MyHttpCallback_OnError(String responseString, ErrorDataModel error)
{}
});
}
convertView = imageView;
return convertView;
}
}
I am happy that the gridView is finally working, but I'm not happy with the fact that it didn't work for me without caching the images. I should have set the imageView's bounds inside the imageAdapter's getView() method before the images were loaded, probably. I will try to fix the gridView without using caching and update my answer if I have found a solution to it in case someone has to face the same problems. Until then, I am glad that I managed to make it work :)
UPDATE:
I finally made it work with and without caching. Here is my updated PhotoGalleryImageAdapter:
public View getView(int position, View convertView, ViewGroup parent) {
final ImageView imageView;
// set the imagge's bounds if it is not loaded yet
if (convertView == null) {
imageView = new ImageView(mContext);
imageView.setLayoutParams(new ViewGroup.LayoutParams(imageSize, imageSize));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(0, 0, 0, 0);
}
else {
imageView = (ImageView) convertView;
}
final String imageKey = photoGalleryModels[position].thumb_image_url;
final Bitmap bitmapImage = mMemoryCache.get(imageKey);
if (bitmapImage != null) {
imageView.setImageBitmap(bitmapImage);
}
else {
imageView.setImageBitmap(emptyBitmap);
DownloadImageWithURL(photoGalleryModels[position].thumb_image_url, new MyHttpCallback() {
#Override
public void MyHttpCallback_OnSuccess(Object data, String responseString) {
if (data instanceof Bitmap) {
mMemoryCache.put(imageKey, (Bitmap)data);
imageView.setImageBitmap((Bitmap)data);
}
}
#Override
public void MyHttpCallback_OnError(String responseString, ErrorDataModel error)
{}
});
}
convertView = imageView;
return convertView;
}
As expected, I needed to set the images bounds before it was loaded.
Because I changed the gridView's numColumns parameter to 'auto_fit', the image's width/height (100dp + stretchMode columnWidth) is calculated as follows:
int imagesPerRow = screenSize.x / (int)(100 * mContext.getResources().getDisplayMetrics().density);
imageSize = screenSize.x / imagesPerRow;
Before loading the imageView's bitmapImage, I create an empty bitmap image and assign it to the imageView (found the code here):
emptyBitmap = Bitmap.createBitmap(imageSize, imageSize, Bitmap.Config.ARGB_8888);
The gridView now works as expected no matter if the LruCache is used or not. I don't know if it's common practice to anser one's own question but in doing so I thought it could help others who facing a similar problem.

Views Navigation Using Swipe Gesture android

Based on json ArrayList size I'm creating TextView's.
By using the class Display , made each TextView height and width to cover the entire screen.
MOTTO
Only 1 TextView should be visible on the screen. By swiping it
should move to next view which will again occupy the entire screen.
Swipe down and Swipe up will move the screens i.e., views... swipe left and swipe right should do some other tasks,such as changing activity
Swipe is enabled by using GestureDetector.SimpleOnGestureListener
So far I've tried using ViewFlipper, TextView array to enable switching between TextView.But FAILED :(
Code snippet:
for(int i=0;i<name.size();i++)
{
text = new TextView(MainActivity.this);
text.setText(name.get(i));
text.setId(i);
text.setTextColor(Color.parseColor("#000000"));
text.setLayoutParams(new LinearLayout.LayoutParams(realWidth, realHeight));
text.setGravity(Gravity.CENTER);
text.setTextSize(40);
text.setClickable(true);
vf.addView(text);
/*
//I've tried the following code while using TextView array
myTextViews[i] = text;
myTextViews[i].setId(i);
myTextViews[i].setTextColor(Color.BLACK);
myTextViews[i].setText(name.get(i));
myTextViews[i].setGravity(Gravity.CENTER);
myTextViews[i].setTextSize(40);
myTextViews[i].setLayoutParams(new LinearLayout.LayoutParams(realWidth, realHeight));
myTextViews[i].onWindowFocusChanged(false);
LL.addView(myTextViews[i]);
*/
View lines = new View(getApplicationContext());
lines.setBackgroundColor(Color.parseColor("#000000"));
lines.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1));
vf.addView(lines);
final int finalI = i;
text.setOnTouchListener(new MainActivity()
{
#Override
public void onSwipeLeft()
{
if (vf.getDisplayedChild() == 0)
Toast.makeText(MainActivity.this, "left", Toast.LENGTH_SHORT).show();
else
vf.showNext();
}
#Override
public void onSwipeRight()
{
if (vf.getDisplayedChild() == 0)
Toast.makeText(MainActivity.this, "right", Toast.LENGTH_SHORT).show();
else
vf.showPrevious();
}
});
}
Errors:
While using ViewFlipper
E/MessageQueue-JNI﹕ Exception in MessageQueue callback: handleReceiveCallback
Array:
E/InputEventReceiver﹕ Exception dispatching input event. -- java.lang.ArrayIndexOutOfBoundsException
EDIT
I found this Question related to ios. Searching the same for android
I'm trying to develop a app similar to SimplEye
which will be used by Visually disabled people.
For that, I need to control the swipes on the screen so that entire app could be handled only through the help of swipes.
ViewPager , ViewFlipper , SimpleOnGestureListener are not matching the requirement.
Kindly suggest what Technique should be used.
Thank you
bases on the question what i can suggest is use ViewPager
which is alternative for your MOTTO not the solutions of your issue
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
ViewPagerActivity
public class ViewPagerActivity extends Activity {
String text[] = {"A", "B",
"C", "D",
"E", "F",
"G", "H"};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyPagerAdapter adapter = new MyPagerAdapter(this, text);
ViewPager myPager = (ViewPager) findViewById(R.id.viewpager);
myPager.setAdapter(adapter);
myPager.setCurrentItem(0);
//set Page Change Listner. to get callback on page changed or swiped
myPager .setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
Log.e("Page Changed ", " YES ");
/// here you can check & perform on changed
Log.e("Current TextView Text ", text[position]);
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
}
}
MyPagerAdapter
public class MyPagerAdapter extends PagerAdapter {
Activity activity;
int txtarray[];
public MyPagerAdapter(Activity act, int[] imgArra) {
txtarray = imgArra;
activity = act;
}
public int getCount() {
return txtarray.length;
}
public Object instantiateItem(View collection, int position) {
TextView view = new TextView(activity);
view.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT));
view.setText(txtarray[position]);
((ViewPager) collection).addView(view, 0);
return view;
}
#Override
public void destroyItem(View arg0, int arg1, Object arg2) {
((ViewPager) arg0).removeView((View) arg2);
}
#Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0 == ((View) arg1);
}
#Override
public Parcelable saveState() {
return null;
}
}
Use ViewPager for sliding the views (only by swipe gesture; if you need swipe during time then ViewFlipper is better approach). Make your ViewPager layout_width/layout_height attrs both match_parent (define ViewPager in xml layout not via code). Also make your TextView layout_width/layout_height match_parent too thus you don't need Display class at all.
EDIT. According to latest edition TS needs to handle gesture navigation across the whole app. I suggest the following:
try to not use any widgets which handle gestures (click) by themselves (button, checkbox etc.). Use only uninteractable Views and implement onTouchEvent method inside your Activity which will receive all touch events in this case. In that method you can add any gesture handling you want.
You can create your own ViewGroup implementation and override there onInterceptTouchEvent/onTouchEvent methods and perform there any gesture handling manually.
In both cases you need to create all gesture detection logic by yourself.
These links may be helpful
Creating a simple Gesture Application in Android
How to add our own gestures in android?
Detecting Common Gestures
Never did it but the first link seems to be the most useful. It says that you can firstly create some kind of gesture description and then gesture API can check any gesture performed for match to that description.

Sliding between fragments

I'm developing an app and currently I am trying to add ScreenSlide to it. I managed to do this using a tutorial, but usual sliding between x pages is not quite what I'm looking for.
With code provided below I can slide between 5 pages, but pages aligned in a straight line and you can't go from first page straight to 5th and vise versa.
In my app I have 4 pages. When I slide left I switch between first 2 pages, when I slide right I switch between 2 last pages. On image below you can see how my current code switches page and under it - my goal.
public class MainActivity extends FragmentActivity {
private static final int NUM_PAGES = 5;
private ViewPager mPager;
private ScreenSlidePageFragment[] pages = new ScreenSlidePageFragment[NUM_PAGES];
private PagerAdapter mPagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_screen_slide);
mPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
}
#Override
public void onBackPressed() {
if (mPager.getCurrentItem() == 0) {
super.onBackPressed();
} else {
mPager.setCurrentItem(mPager.getCurrentItem() - 1);
}
}
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return NUM_PAGES;
}
#Override
public Fragment getItem(int position) {
boolean moveRight = mPager.getCurrentItem() < position;
boolean moveLeft = mPager.getCurrentItem() > position;
switch(position){
case 5:
if(moveRight){
return geLog.w("i"
+ "Info", Integer.toString(position));
//return getPageByPosition(2);
if(moveLeft)
return getPageByPosition(2);
}
return new ScreenSlidePageFragment();
}
private Fragment getPageByPosition(int position){
int index = position - 1;
if(index < 0 || index > NUM_PAGES-1)
throw new InvalidParameterException("requested position is invalid");
if(pages[index] == null)
pages[index] = new ScreenSlidePageFragment();
return pages[index];
}
}
}
[UPDATE]
I've managed to write a code that allows me to infinitely slide to the right between 6 different pages. Left side is limited though - I can slide only to the first page(so if I'm on 1st page after I cycled 3 times to the right, I can make only 3 cycles backwards). I think I am very close to finding the solution. Any ideas?
MainActivity.java
public class MainActivity extends FragmentActivity {
private static final int NUM_PAGES = 6;
private ViewPager pager;
private PagerAdapter pagerAdapter;
private List<ScreenSlidePageFragment> slideList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
slideList = new ArrayList<ScreenSlidePageFragment>();
for (int i = 0; i<NUM_PAGES; i++){
ScreenSlidePageFragment slide = new ScreenSlidePageFragment();
slide.setIndex(i+1);
slideList.add(slide);
}
pager = (ViewPager) findViewById(R.id.pager);
pagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
pager.setAdapter(pagerAdapter);
}
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
int _pos = position % NUM_PAGES;
return slideList.get(_pos);
}
#Override
public int getCount() {
return Integer.MAX_VALUE;
}
}
}
ScreenSlidePageFragment.java
public class ScreenSlidePageFragment extends Fragment {
private int index;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ViewGroup root = (ViewGroup)inflater.inflate(R.layout.slide, container, false);
TextView tw = (TextView) root.findViewById(R.id.textView);
tw.setText(Integer.toString(index));
return root;
}
public void setIndex(int index){
this.index = index;
}
}
One possible workaround is not using viewpager, but creating your own custom Slider, I ended up with this solution, since ViewPager is very unflexible, like for example if you try to do something like Facebook Page app kind of thing.
Cons: you have to manage all the touches and lifecycle.
Another simpler solution for this is basically use the following API if I understand the question correctly it should be enough.
ViewPager.setCurrentItem
This is what I would do.
Since you have a finite amount of screens, I would make an array to save each instance of the ScreenSlidePageFragment you make under a corresponding index. So you can get an instance from that array by using "position". This will allow you to leverage getItem function to do something like
...
private ScreenSlidePageFragment[] pages = new ScreenSlidePageFragment[NUM_PAGES];
...
#Override
public Fragment getItem(int position) {
boolean moveRight = mPager.getCurrentItem() < position;
switch(position){
case 1:
if(moveRight)
return getPageByPosition(3);
return getPageByPosition(2);
case 2:
if(moveRight)
//case when you are in a position 3 and swipe right - return first screen
return getPageByPosition(3);
//if you're swiping left from position 1, return the next screen
return getPageByPosition(1);
case 3:
//write out the rest of your logic
...
}
}
private Fragment getPageByPosition(int position){
int index = position - 1;
if(index < 0 || index > NUM_PAGES-1)
throw new InvalidParameterException("requested position is invalid")
if(pages[index] == null)
pages[index] = new ScreenSlidePageFragment();
return pages[index];
}
Note: code not tested
Basically, you will need to write out the logic to return an existing instance of a fragment based on where you are currently and which position is requested.
You can try ViewPager.setCurrentItem
If you want to navigate to page
viewPager.setCurrentItem(PAGE_POS);
If you want to navigate to page without smooth scroll
viewPager.setCurrentItem(PAGE_POS,false);
I think it's possible to do.
1. Set ViewPager's item count to Integer.MAX_VALUE and current item to Integer.MAX_VALUE/2. It gives you ability to create fake infinite scroll.
2. Realize your logic depending on fragment on current position. It is easy if you store them in your adapter:
#Override
public Fragment getItem(int position) {
ItemFragmentRoot fragment = new ItemFragmentRoot();
mFragmentMap.put(position, fragment);
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
mFragmentMap.remove(position);
}
public ItemFragmentRoot getItem(int position){
return mFragmentMap.get(position);
}
And the most difficult part, I think. Since ViewPager cache at least 1 view each side and you don't know which way user going to scroll the only one way is to manualy initialize and reinitialize appearing fragment according to scroll side. Use nested fragment inside each ItemFragmentRoot I mentioned before. All depends on how heavy your view hierarchy.
I can write more code later, if you need.

Change fragment visibility in runtime when the fragment was created using FragmentPagerAdapter

I'm trying to create my first Android app that looks like following: there is main activity with multiple fragments initialized by FragmentPagerAdapter. There is another activity (SettingsActivity) where I want to list all the fragment names and allow hiding some of them. To hide them I want to use the following:
FragmentManager fm=getFragmentManager();
Fragment myFragment=fm.findFragmentByTag("tag");
fm.beginTransaction().hide(myFragment).commit();
The problem is that I don't know fragment id or tag, not sure if they exist. How I can get them? Should I switch to XML definition to make it possible?
Adapter:
public class TabsPagerAdapter extends FragmentPagerAdapter {
public TabsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
switch (index){
case 0:
return new CoverFragment();
case 1:
return new NumbersConverterFragment();
case 2:
return new TempConverterFragment();
case 3:
return new LengthConverterFragment();
case 4:
return new AreaConverterFragment();
case 5:
return new VolumeConverterFragment();
case 6:
return new WeightConverterFragment();
case 7:
return new SpeedConverterFragment();
}
return null;
}
#Override
public int getCount() {
return 8;
}
Main activity:
public class MainActivity extends FragmentActivity implements ActionBar.TabListener {
private ViewPager viewPager;
private TabsPagerAdapter tabsPagerAdapter;
private ActionBar actionBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
String[] tabs={getString(R.string.title_section0), getString(R.string.title_section1),getString(R.string.title_section2)};
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager=(ViewPager) findViewById(R.id.pager);
actionBar=getActionBar();
tabsPagerAdapter=new TabsPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(tabsPagerAdapter);
actionBar.setHomeButtonEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
for(String tab : tabs){
actionBar.addTab(actionBar.newTab().setText(tab).setTabListener(this));
}
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// on changing the page
// make respected tab selected
actionBar.setSelectedNavigationItem(position);
}
...
});
}
Fragment layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#fbfdfb"
>
<TextView android:text="#string/celsius_" android:id="#+id/textView1"
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
<EditText android:text="" android:id="#+id/txtCelsius" android:layout_width="match_parent"
android:layout_height="wrap_content"></EditText>
<TextView android:text="#string/fahrenheit_" android:id="#+id/textView1"
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
<EditText android:text="" android:id="#+id/txtFahrenheit" android:layout_width="match_parent"
android:layout_height="wrap_content"></EditText>
<TextView android:text="#string/kelvin_" android:id="#+id/textView1"
android:layout_width="wrap_content" android:layout_height="wrap_content"></TextView>
<EditText android:text="" android:id="#+id/txtKelvin" android:layout_width="match_parent"
android:layout_height="wrap_content"></EditText>
</LinearLayout>
Fragment class:
public class TempConverterFragment extends Fragment {
EditText txtCelsius, txtFahrenheit, txtKelvin;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.temp_converter_fragment, container, false);
txtCelsius = (EditText) rootView.findViewById(R.id.txtCelsius);
txtFahrenheit = (EditText) rootView.findViewById(R.id.txtFahrenheit);
txtKelvin = (EditText) rootView.findViewById(R.id.txtKelvin);
...
}
...
}
Thanks in advance.
If SettingsActivity is not the Activity holding the FragmentPagerAdapter, then you would have to re-create all the fragments. The nature of a fragment is to be closely tied to it's activity.
If SettingsActivity is the Activity holding the FragmentPagerAdapter, then As I recall, FragmentPagerAdapter will initialize all the 8 fragments as soon as possible to have them ready when you swipe, unlike FragmentStatePagerAdapter. This means that you should (I think) be able to create each fragment in the constructor TabsPagerAdapter and keeping a reference to them, which you could access using getter methods on the TabsPagerAdapter.
Here is an example of how to get easy access to your pageradapter fragments:
public class DisplayPagerAdapter extends FragmentStatePagerAdapter {
private static final String TAG = "DisplayPagerAdapter";
SparseArray<DisplayFragment> registeredFragments = new SparseArray<DisplayFragment>();
#Inject DisplayCoreModule display;
public DisplayPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return (display != null && display.getPagesCount() > 0) ? display.getPagesCount() : 1;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
Log.d(TAG, "getItem " + position);
return DisplayFragment.newInstance(position);
}
#Override
public CharSequence getPageTitle(int position) {
if (display != null && display.getPagesCount() > 0) {
return "Side " + (position+1);
} else {
return super.getPageTitle(position);
}
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Log.d(TAG, "instantiateItem " + position);
DisplayFragment fragment = (DisplayFragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
Log.d(TAG, "destroyItem " + position);
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
public SparseArray<DisplayFragment> getRegisteredFragments() {
return registeredFragments;
}
}
Now if you implement this usage of registeredFragments , you can call tabsPagerAdapter.getRegisteredFragment(2) to get your TempConverterFragment.
SparseArray<DisplayFragment> should be SparseArray<Fragment> in your case
Now this does not solve the your SettingsActivity problem. But if I understand you correctly, then adding the fragments your want directly in the layout XML of SettingsActivity would make sense. Then it would be easy to temporarily hide the fragments or whatever using:
FragmentManager fm=getFragmentManager();
Fragment myFragment=fm.findFragmentById(R.id.frag_tempconverter)
fm.beginTransaction().hide(myFragment).commit();
Notice the use of findFragmentById. The tag is usually used for dynamically added fragments (atleast in my mind). The findFragmentById will surely return a fragment if it is defined in the XML layout but just to be clear, it will be a new instance of the fragment.
To address your questions:
What if I move the fragments to the main activity XML? Won't it make things simpler
Do not think so, the updated answer shows how to easily access the fragments (from within your main activity).
Though not sure I can use FragmentManager in SettingsActivity
Sure you can. You can add new fragments, access available fragments (from predefined XML using findById or dynamically added using findByTag). You cannot, however, access the same instance of the fragment as was kept by your main activity.
To share information between the fragments and the two activities, you need to persist the state of your fragments somehow (which is a different topic).
All in all I think you are on the right path, you just need to combine the right pieces of the puzzle :)

Categories