I want to use three previously created Activities in a ViewPager (with PagerAdapter) so the user can scroll smoothly horizontally through them.
I followed a tutorial which worked great. The problem is in the tutorial they use TextViews to demonstrate. I already have finished Activities (of which the layouts are in XML files). I want to use these Activities in that slider now but it looks like I can only use Views for that.
I could not figure out how I have to change the code of the classes (from "implements Activity" to "extends View") that I can use it in the slider.
The my current code looks like this:
public class HorizontalSliderBeispielActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
cxt = this;
awesomeAdapter = new AwesomePagerAdapter();
awesomePager = (ViewPager) findViewById(R.id.awesomepager);
awesomePager.setAdapter(awesomeAdapter);
}
...
then the inner class with the PageAdapter:
...
private class AwesomePagerAdapter extends PagerAdapter {
public Object instantiateItem(View collection, int position) {
TextView tv = new TextView(cxt);
tv.setText("Bonjour PAUG " + position);
tv.setTextColor(Color.WHITE);
tv.setTextSize(30);
view_01 = new SubmitCheatInstructions(cxt);
((ViewPager) collection).addView(tv, 0);
((ViewPager) collection).addView(view_01 , 1);
return tv;
}
}
Instead of that TextView "tv" I want to use Activities (i.e. SubmitCheatInstructions). Previously this class looked like:
public class SubmitCheatInstructions implements Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.submitcheat_instructions);
}
}
but as far as I know I have to change it to
public class SubmitCheatInstructions extends View {
????
}
in order to be able to use it for the ViewPager.
My problem now is that I want to load the layout from the layout xml file (submitcheat_instructions.xml) into that view and not do everything in code. I could not figure out how I have to do this.
Thanks for any help.
I followed the same tutorial and like you i was unable to achieve this at first but after some tinkering and trial and error the following code worked like a charm:
/**
* Create the page for the given position. The adapter is responsible
* for adding the view to the container given here, although it only
* must ensure this is done by the time it returns from
* {#link #finishUpdate()}.
*
* #param container
* The containing View in which the page will be shown.
* #param position
* The page position to be instantiated.
* #return Returns an Object representing the new page. This does not
* need to be a View, but can be some other container of the
* page.
*/
#Override
public Object instantiateItem(View collection, int position) {
View v = new View(PatientView.this.getApplicationContext());
final LayoutInflater inflater = (LayoutInflater) PatientView.this
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
switch (position) {
case 0:
v = inflater.inflate(R.layout.patientp1,
(ViewGroup) null, false);
((Button) v.findViewById(R.id.pp1btnbck)).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
finish();
}
});
break;
case 1:
v = inflater.inflate(R.layout.patientp2, null, false
);
break;
default:
TextView tv = new TextView(PatientView.this.context);
tv.setText("Page " + position);
tv.setTextColor(Color.WHITE);
tv.setTextSize(30);
v = tv;
break;
}
((ViewPager) collection).addView(v, 0);
return v;
}
Hope this helps, Good luck
Shereef Marzouk
Thanks Shereef. Your code was really useful.
I wanted to pass data from my main activity to the different layout's in ViewPager. I got the idea from your code where you are setting the onClickListener for the button. That helped me to pass data generated in the main activity to the different TextView's in different layout's.
public Object instantiateItem(View collection, int position)
{
View v = new View(CalcTimeActivity.this.getApplicationContext());
collection.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final LayoutInflater inflater = (LayoutInflater) CalcTimeActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
switch (position) {
case 0:
v = inflater.inflate(R.layout.N, (ViewGroup) null, false);
((TextView) v.findViewById(R.id.tvA)).setText(strA);
((TextView) v.findViewById(R.id.tvB)).setText(strB);
((TextView) v.findViewById(R.id.tvC)).setText(strC);
break;
case 1:
v = inflater.inflate(R.layout.M, null, false);
break;
case 2:
v = inflater.inflate(R.layout.F, null, false);
break;
}
((ViewPager) collection).addView(v, 0);
return v;
}
Related
I have a viewPager with say 4 pages. All 4 pages uses same Xml. When i do an event in 1st page somehow it always triggers in the last page.
Here is my PagerAdapter
#Override
public Object instantiateItem(ViewGroup container, int pos) {
View desktopView;
OnTouchListener tl = null;
desktopView = act.getLayoutInflater().inflate(
act.getViewPagerLayout(groupName), null);
RelativeLayout rr_appContainer, rr_dialogContainer;
ImageView rr_home_container = (ImageView) desktopView
.findViewById(R.id.imageView_forClick);
Button buttonChange = (Button)desktopView.findViewById(R.id.B1);
Button buttonDelete = (Button)desktopView.findViewById(R.id.B2);
rr_appContainer = (RelativeLayout) desktopView
.findViewById(R.id.rr_home_container);
rr_dialogContainer = (RelativeLayout) desktopView
.findViewById(R.id.rr_dialogView);
..........
buttonDelete.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
deletestuff();
}
buttonChange.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
changeColorOfStuff();
}
.....
return desktopView;
}
What is happening is, When i click on buttonChange from 1st page it supposed to change the color of text on 1st page, but actually it is changing color of the last page. Similarly buttonDelete is deleting color from last page.
Regardless of what page i am in, its reflecting those changes on last page.
Any help would be appreciated.
From the context given here, the deleteStuff() and changeColorOfStuff() can only be members of the Fragment/Activity that owns the adapter, or the adapter itself. So these methods can only act on members of those classes. ViewPager asks the adapter for the fragments it is going to display. However, the text in the fragment being shown by the ViewPager belong to the that fragment. To act on that text, you need a method that's a member of that fragment. The usual way to do this is to use a custom fragment. For example:
Custom Fragment (inner class):
public static class CustomFragment extends Fragment {
//members of the fragment
TextView yourTextView;
...
public static CustomFragment newInstance(int pos) {
CustomFragment fragment = new CustomFragment();
//get whatever info you need for this page
Bundle args = getInfoSomehow(pos);
fragment.setArguments(args)
return fragment;
}
#Override
public View onCreateView(Layout inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(....
yourTextView = root.findViewById(...) //this is the text view you want to change things in
//all the stuff you're currently doing in instantiateItem()
return root;
}
private void deleteStuff() {
//whatever you need to do. But notice that here it's acting on the TextView that belongs to this particular fragment
}
private void changeColorOfStuff() {...}
...
}
Then in your instantiateItem(...)
#Override
public Object instantiateItem(ViewGroup container, int pos) {
return CustomFragment.newInstance(pos);
}
I have a demo activity, which is essentially a collection of 5 images. I have these saved in my /drawable directory. I am using a custom implementation of FragmentPagerAdapter because I have a static set of Fragments to page through:
private class MyPagerAdapter extends FragmentPagerAdapter {
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int pos) {
return SDemoFragment.newInstance(pos);
}
#Override
public int getCount() {
return 5;
}
}
I have a custom implementation of Fragment as well. Currently, I have a single ImageView layout which I inflate, and depending on mNum, the image source is set to one of my image drawables.
public class SDemoFragment extends Fragment {
int mNum = 0;
/**
* Create a new instance of CountingFragment, providing "num" as an
* argument.
*/
public static SDemoFragment newInstance(int num) {
SDemoFragment f = new SDemoFragment();
// Supply num input as an argument.
Bundle args = new Bundle();
args.putInt("num", num);
f.setArguments(args);
return f;
}
/**
* When creating, retrieve this instance's number from its arguments.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null)
mNum = getArguments().getInt("num");
}
/**
* The Fragment is created here.
*/
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.pager_item_fragment, container,
false);
ImageView x = (ImageView) v.findViewById(R.id.pagerItemImage);
switch (mNum) {
case 0:
x.setImageResource(R.drawable.demo0);
break;
case 1:
x.setImageResource(R.drawable.demo1);
break;
case 2:
x.setImageResource(R.drawable.demo2);
break;
case 3:
x.setImageResource(R.drawable.demo3);
break;
case 4:
x.setImageResource(R.drawable.demo4);
break;
}
return x;
}
}
Code for the simple image layout:
pager_item_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pagerItemImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="Tutorial"
android:src="#drawable/demo0" />
Is there any reason why every time I swipe from one image to the next, it lags? Is there an issue with memory, or using the right method, or calling inflater.inflate(...); every time onCreateView() is called? Any help would be appreciated, and if you need more sections of my code, don't hesitate to ask. Thanks!
Update: I'm now regularly getting an OutOfMemoryException too.
You can use the ViewHolder pattern for minimizing the view lookups. Also offloading the image decoding to an AsyncTask would help a lot:
public View getView(int position, View v, ViewGroup parent) {
// Need to do this only once per each view.
ViewHolder viewHolder = new ViewHolder();
holder.mView = v.findViewById(R.id.pagerItemImage);
holder.mPosition = position;
new GetImageTask(position, holder).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null);
return v;
}
private static class GetImageTask extends AsyncTask {
private int mPosition;
private ViewHolder mHolder;
public GetImageTask(int pos, ViewHolder holder) {
mPosition = pos;
mHolder = holder;
}
#Override
protected Bitmap doInBackground(Void... arg0) {
return BitmapFactory.decodeResource(getResources(), R.drawable.image);
}
#Override
protected void onPostExecute(Bitmap bitmap) {
// Update the appropriate slide
if (mHolder.position == mPosition) {
// To be reconsidered. You can either set the returned image at runtime, and set it
// for your view, or you can store it inside the ViewHolder.
mHolder.view.setImageBitmap(bitmap);
}
}
}
// This will hold all the data for you views.
private static class ViewHolder {
private Bitmap mBitmap;
private int mPosition;
private View mView;
}
Please mind that this will store the bitmaps. Quite possibly you'll get an OutOfMemory at some points.
More decent way to approach this would be required. For example, you should store only your current view, and its neighbours at some offset.
THis pseudo-code may be a bit not off-the-shelve but should be enough to display the general idea. For details on ViewHolders please see Smooth Scrollin - Android Developer, as view pager/flipper will be almost the same case as listview.
I realized my error thanks to #2Dee's link to the Android Developer Guide on Bitmap Loading: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
I moved the loading of all my Bitmaps to the parent Activity, and loaded them in an AsyncTask.
i have set up an pp using the default tabs + swipe layout, i figured out how to display different content on different slides,
the problem im having is how i can start an intent (make a phone call using an imagebutton.
the image button is is fragment 1 or slide 1, i dont know where to declre the button, or how to format the code,
best i have managed is 1 error stating:
Gradle: error: non-static method findViewById(int) cannot be referenced from a static context
my code is as follows,
/**
* A dummy fragment representing a section of the app, but that simply
* displays dummy text.
*/
public static class DummySectionFragment extends Fragment {
/**
* The fragment argument representing the section number for this
* fragment.
*/
public static final String ARG_SECTION_NUMBER = "section_number";
public DummySectionFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if ((getArguments().getInt(ARG_SECTION_NUMBER)==1)) {
View view = inflater.inflate(R.layout.test, container, false);
return view;
ImageButton ib1;
ib1 = (ImageButton) findViewById(R.id.ib1);
// add button listener
ib1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:0377778888"));
startActivity(callIntent);
}
});
}
if ((getArguments().getInt(ARG_SECTION_NUMBER)==2)) {
View view = inflater.inflate(R.layout.test2, container, false);
return view;
}
if ((getArguments().getInt(ARG_SECTION_NUMBER)==3)) {
View view = inflater.inflate(R.layout.test3, container, false);
return view;
}
else {
TextView textView = new TextView(getActivity());
textView.setGravity(Gravity.CENTER);
textView.setText(Integer.toString(getArguments().getInt(ARG_SECTION_NUMBER)));
return textView;
}
}
}
}
maybe remove the static in the DummySectionFragment declaration if this is not an inner class.
Also the findViewById should be getView().findViewById. Im no expert on Fragment but try this.
First, this is my MainPageViewer:
super.onCreate(savedInstanceState);
setContentView(R.layout.viewpager);
MyPageAdapter adapter = new MyPageAdapter();
ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(adapter);
pager.setCurrentItem(0);
The Adapter for my PageViewer is here:
public int getCount()
{
return 3;
}
public Object instantiateItem(View collection, int position)
{
LayoutInflater inflater = (LayoutInflater) collection.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
int resId = 0;
switch(position)
{
case 0:
resId = R.layout.blogandbericht;
break;
case 1:
resId = R.layout.blogandbericht;
break;
case 2:
resId = R.layout.blogandbericht;
break;
}
View view = inflater.inflate(resId, null);
((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;
}
(Yes i know, that all layout are "bloganbericht")
And this my blogandbericht:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/masterSv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#8ad5f0">
<LinearLayout
android:id="#+id/masterLl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="foo bar"/>
</LinearLayout>
</ScrollView>
all works fine. I can see the three buttons on each layout.
But how can i add views like textview or button to the layout dynamicly?
Well the viewpager only supports 1 view inside each page, so in the adapter you modify the method:
public Object instantiateItem( View pager, int position )
you can create the view inside this method and return it to like this:
WebView web = new WebView(mContext);
return web;
Or if you need a more complex view than just an "Object" then you can create a linear layout, or something similar and add your other views inside and return that view like this:
LinearLayout layout = new LinearLayout(mContext);
WebView web = new WebView(mContext);
Button btn = new Button(mContext);
layout.addView(web);
layout.addView(btn);
return layout;
Hope it helps.
UPDATE:
Well right now i am doing something similar, i make the view that i will add to the viewpager dynamic, so depending on the type of an object i determine what is going to be inside.
I do not know how you plan to determine how a page must look, but i will tell you how i do it.
I have a list of "Objects", and those objects extends from a Class (You can also do it with interface and implementation, i do it with extends for other reasons) in common, and that class have a method "public View getView()" and the in all the Classes that extends from the Father Class (or Implemented the Interface), you Override (Implement) the method and add the code necessary to make that "PAGE" look like you want, then on the adapter you only need to have a list of "Objects" of any of the classes that you created and depending on which type each of those "Objects" are they will create the View passed to the ViewPager.
EXAMPLE:
Dog //CLASS OR INTERFACE WITH METHOD getView()
-Chihuahua //This return a WebView
-Sharpei //This return a HorizontalListView
-Huskie //This return a LinerLayout with a Bunch of Views Inside
on your adapter:
public class ViewPagerAdapter extends PagerAdapter {
List<Dog> Doggies = new ArrayList<Dog>();
public ViewPagerAdapter( Context context ){
Doggies.add(new Chihuahua());
Doggies.add(new Sharpei());
Doggies.add(new Chihuahua());
Doggies.add(new Huskie());
Doggies.add(new Huskie());
Doggies.add(new Chihuahua());
Doggies.add(new Sharpei());
}
//and last but not least, on your onCreate method in your adapter:
public Object instantiateItem( View pager, int position ){
return Doggies.get(position).getView();
}
/*THE REST OF THE METHODS GOES AFTER THIS*/
} //END OF ADAPTER
I'm trying to implement ViewPager to combine two view within one activity, in which I'm facing NullPointerException when I'm trying to access items using findViewById in inflated views, my custom page adapter is as follows,
public class CustomPagerAdapter extends PagerAdapter {
#Override
public int getCount() {
return 2;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == ((View) object);
}
#Override
public void destroyItem(View view, int value, Object object) {
((ViewPager) view).removeView((View) object);
}
#Override
public Parcelable saveState() {
return null;
}
#Override
public Object instantiateItem(View collection, int position) {
LayoutInflater layoutInflater = (LayoutInflater) collection.getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
int resultId = 0;
switch (position) {
case 1:
resultId = R.layout.layout2;
break;
case 0:
default:
resultId = R.layout.layout1;
break;
}
View view = layoutInflater.inflate(resultId, null);
((ViewPager) collection).addView(view, 0);
return view;
}
}
And my activity is as follows, in which I'm trying to access button which is inside layout1 which throws null value.
public class PageSwiperActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.viewpagerlayout);
Button myButton=(Button) findViewById(R.id.myButton);
}
}
My viewpagerlayout.xml contains only,
<android.support.v4.view.ViewPager
android:id="#+id/viewPagerLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
and my layout1.xml contains one button. Give me some guidance.
Thanks.
Did you set your PageAdapter to ViewPager?
If you don't set it, then remove line with button and add this code to your activity:
final ViewPager viewPager = (ViewPager) findViewById(R.id.viewPagerLayout);
viewPager.setAdapter(new CustomPagerAdapter());
instead of this
Button myButton = (Button) findViewById(R.id.myButton);
You can call findViewById to get your button in this method of your adapter:
#Override
public Object instantiateItem(View collection, int position) {
... // your previous code
final Button myButton = (Button) findViewById(R.id.myButton);
return view;
}
after you have inflated layout which contains this button.
After setContentView(R.layout.viewpagerlayout);
you are using
Button myButton=(Button) findViewById(R.id.myButton); directly.
But actually viewpagerlayout doesn't have any view with id myButton.
So that you are getting NULL Pointer Exception. Before using findViewById, you have to add views to ViewPager layout.
In the code you posted here, it seem to be you are adding, but you have to use it after setContentView.