I am trying to do something like the android market to show products from different categories. I have implemented that behavior with this library, and it's all running good.
What I want to do now is to have a list view in the page of the categories, and in each one of the others a grid view. Of course, each one of them will have different sources of data.
How can I achieve that ? Does it have anything to do with adapters?
I have find one Very good Example you can find that on this site
https://github.com/astuetz/ViewPagerExtensions
Else hope this code will be Helpful to you.
#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;
}
I think you want extend FragmentPagerAdapter, override getItem, and return the proper Fragment object (or ListFragment) depending on the index/position in the ViewPager.
In the layout xml for your Activity you should include a ViewPager (<android.support.v4.view.ViewPager>), and in your Activity onCreate use the setAdapter method of ViewPager to assign your FragmentPagerAdapter.
Related
This question has been edited for the second time: To view the original question, please Scroll down to where it says "ORIGINAL QUESTION" - for any edits after that - Please scroll down to an "EDIT #" section.
EDIT TWO:
This edit is in reponse to Luksprog's request to see where I set elements required in getView() - After checks for convertView being null or not.
Note : Please understand that this code is semi-modular.
CODE:
Holder Patterns Used:
public static class HeaderHolder
{
public TextView textView;
}
public static class CommandHolder
{
public TextView textView;
public Button[] buttonHolder;
public OnClickListener[] clickHolder;
}
getView() Method - Where the setting takes place:
switch(type)
{
case 0: // Is a header
String temp = commandLabel.replace("Section:", "");
headerHolder.textView.setText(temp);
break;
case 1:// Is a command
commandHolder.textView.setText(commandLabel);
ArrayList<String[]> commands = commandCreator.getCommands();
// Initially set the visibility of buttons to GONE
for (int i = 0; i <commandHolder.buttonHolder.length; i++)
{
commandHolder.buttonHolder[i].setVisibility(View.GONE);
}
// Only show buttons based on how many commands there are
for (int i = 0; i < commands.size(); i++)
{
pos = i;
commandHolder.buttonHolder[i].setVisibility(View.VISIBLE);
drawable_normal = commands.get(i)[1];
drawable_pressed = commands.get(i)[1] + "_pressed";
buttonStates = new StateListDrawable();
buttonStates.addState(new int[]{statePressed}, ApplicationConstants.moduleImageLoader.findImageByName(drawable_pressed));
buttonStates.addState(new int[]{-statePressed}, ApplicationConstants.moduleImageLoader.findImageByName(drawable_normal));
buttonStates.addState(new int[]{}, ApplicationConstants.moduleImageLoader.findImageByName(drawable_normal));
commandHolder.buttonHolder[i].setBackgroundDrawable(buttonStates);
// Retrieve the intent and parameter from the current command
String parameter = commands.get(i)[2];
String intentName = commands.get(i)[0];
if(intentName.contains("Call Phone"))
{
commandHolder.clickHolder[i] = new OnClickListener()
{
public void onClick(View arg0)
{
//con.startActivity(call_phone);
}
};
}
else if(intentName.contains("Cell"))
{
commandHolder.clickHolder[i] = new OnClickListener()
{
public void onClick(View arg0)
{
//con.startActivity(call_cell);
}
};
}
else if(intentName.contains("Map"))
{
commandHolder.clickHolder[i] = new OnClickListener()
{
public void onClick(View arg0)
{
//con.startActivity(load_map);
}
};
}
else if(intentName.contains("Email"))
{
commandHolder.clickHolder[i] = new OnClickListener()
{
public void onClick(View arg0)
{
//con.startActivity(send_email);
}};
}
commandHolder.buttonHolder[i].setOnClickListener(commandHolder.clickHolder[i]);
}
convertView.setOnClickListener(new OnClickListener()
{
public void onClick(View arg0)
{
Dialog dialog = new Dialog(ApplicationConstants.ref_currentActivity);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
LinearLayout listDialogBoxlayout = new ListDialogBox(con, commandType, commandLabel, commandInfo);
dialog.setContentView(listDialogBoxlayout);
dialog.setCancelable(true);
dialog.show();
}
});
break;
}
return convertView;
Question : To see the problem occurring visually. Please scroll down to EDIT ONE and look at the screen shots.
If anyone has any questions, or needs clarification, please leave a comment and I will get back to you.
EDIT ONE:
Made Changes , but still the same behaviour
Here's the new implementation of the getView() method using getItemViewType():
I do the following checks:
if(convertView == null)
{
switch type
case header:
convertView = inflate a header
headerHolder.textView = convertView.findViewById(headerTextId);
convertView.setTag(headerHolder);
break;
case command:
convertView = inflate a command
// Use CommandHolder to initialize all the necessary elements
commandHolder.textView = convertView.findViewById(commandLabel);
.
.
convertView.setTag(commandHolder);
break;
}
else
{
switch(type)
case header :
headerHolder = convertView.getTag();
break;
case command :
commandHolder = convertView.getTag();
break;
}
// set the actual elements
switch(type)
case:header
//set all elements in header
break;
case: command
//set all elements in command
break;
return convertView;
What's happening:
I'm a visual person, so I thought this would help:
Here's some screen shots:
No scrolling - Everything looks great
Scrolling with mouse scroll, everything still looks great
Scrolling by clicking on the list and dragging it down - Notice how canam is now at the bottom, where Linda should be?
Scrolling by clicking on the list and dragging it up - Notice how peter is at the top where canam should be?
List of what should be there:
Address Header
Canam
Employees Header
Dave
Brent
Stephen
Moacir
Peter
Linda
Even though the views get messed up - clicking the views shows the correct dialog box pop up- As you can see above.
Here's whats not happening:
No repeating views
No section headers being drawn in the wrong places
No section headers being drawn as commands(which can be addresses or contacts).
I feel like I'm right there guys..
What could I have missed?
Original Question:
This is currently what I am doing: I've simplified the situation to really just ask my question with convertView and not possibly confuse people with the rest of my ViewCreators.
Methods that my ListViewAdapter has:
getItemViewType(...)
getViewTypeCount(...)
getView(int position, View convertView, ViewGroup parent)
{
int type = getItemViewType(position);
if(listItem is section)
{
if(convertView == null)
{
ViewGroup viewGroup = inflate a view
Holder.textView = textview;
viewgroup.setTag(holder);
view = viewGroup;
}
else
{
holder = (HeaderHolder) convertView.getTag();
view = convertView;
}
}
else
// different layout. Complicated list item in the form:
//|TextView Button1 Button2 Button3| , where buttons only appear based on
//a CommandCreator. If john(TextView) has a phone and email - it displays:
// |John PhoneButton EmailButton|
{
if(convertView == null)
{
// inflate view
// make buttons
// make onclicks null
// set different holder
viewgroup.setTag(holder)
view = viewgroup;
}
else
{
view = convertView;
holder = (CommandHolder) convertView.getTag();
}
// set the buttons to show based on commands
// set the onclick listeners to the buttons to call certain intents based on the command
}
return view;
}
}
Question:
How should I use getItemViewType and getViewTypeCount? and How do I use Viewgroups, and my current holders? Would the following work?
getView
{
type = getItemViewType;
if(convertView == null)
{
switch(type)
case: header
convertView = inflate header view
intialize HeaderHolder
break;
case: command
convertView = inflate command view
initialize CommandHolder
break;
}
else
{
switch(type)
case: header
//Actually set all the HeaderHolder Stuff
String temp = commandLabel.replace("Section:", "");
holder.textView.setText(temp);
LinearLayout.LayoutParams textViewParams = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT);
holder.textView.setLayoutParams(textViewParams);
return view;
QUESTION : But how do I use Viewgroups, and my holder. and what am I returning?
break;
case: command
Actually set all the commandHolder stuff
QUESTION : Same as above.
break;
}
return convertView?
}
Here are my Handlers now:
public static class HeaderHolder
{
public TextView textView;
}
public static class CommandHolder
{
public TextView textView;
public Button[] buttonHolder;
public OnClickListener[] clickHolder;
}
Thank you for all your help in advance! and I will answer any questions you may have.
About those two methods:
getViewTypeCount() return the number of types of layouts, 2 in your case
getItemViewType() based on the int parameter supplied you either return 0(for the header layout for example) or 1(for the normal row layout). This really depends on your data and how you setup the headers and normal rows in the list.
Regarding your last edit, you probably don't set the data on the row layout like you should, especially as you have references to those OnClickListeners. You should post the full code from this block:
// set the actual elements
switch(type)
case:header
//set all elements in header
break;
case: command
//set all elements in command
break;
Here is a sample code I wrote with an example implementing multiple ViewHolders and different row layouts types(if you have problems regarding this aspect of the adapters).
try this code may this will help you...
if(convertView == null)
{
switch type
case header:
convertView = inflate a header
headerHolder.textView = convertView.findViewById(headerTextId);
convertView.setTag(headerHolder);
break;
case command:
convertView = inflate a command
// Use CommandHolder to initialize all the necessary elements
commandHolder.textView = convertView.findViewById(commandLabel);
.
.
convertView.setTag(commandHolder);
break;
}
else
{
switch(type)
case header : {
if(convertView.getTag() instanceOf HeaderHolder){
headerHolder = convertView.getTag();
} else {
headerHolder = inflate a header
convertView.setTag(headerHolder);
}
}
break;
case command : {
commandHolder = convertView.getTag();
if(convertView.getTag() instanceOf CommandHolder){
commandHolder = convertView.getTag();
} else {
commandHolder = inflate a command
convertView.setTag(commandHolder );
}
}
break;
}
// set the actual elements
switch(type)
case:header
//set all elements in header
break;
case: command
//set all elements in command
break;
return convertView;
I have a working viewpager example using code I got from a tutorial. The tutorial was written by Lauren Darcey and Shane Conder who are apparently experts (I am new to Android). The code is pasted below. What this code is doing is inflating (ahead) and destroying (behind) as the user swipes horizontally. There are only 5 pages. It seems that it would be much smarter to inflate them all and then let the user do all the swiping with no inflation/destruction going on. Just like it were one big wide page (such as the Panarama and Pivot controls in Windows Phone 7).
Also, this code blows if one of the pages has a google map on it. It blows on the second inflation (don't know why yet) and this wouldn't happen if they were just all inflated once.
Is there a reason why it has to be done this way? Are there any examples available on doing the way I suggest?
Thanks,
Gary Blakely
private class MyPagerAdapter extends PagerAdapter {
public int getCount() {
return 5;
}
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.farleft;
break;
case 1:
resId = R.layout.left;
break;
case 2:
resId = R.layout.middle;
break;
case 3:
resId = R.layout.right;
break;
case 4:
resId = R.layout.farright;
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);
}
You can use ViewPager.setOffscreenPageLimit(int) to load all off-screen tabs. Just set it to 4 (that's 5 total, minus 1 visible) by doing ((ViewPager) collection).setOffscreenPageLimit(4); or ((ViewPager) collection).setOffscreenPageLimit(getCount() - 1);.
Keep in mind the memory implications of this; having everything loaded will run down the device's RAM if you're not careful.
I know this may sound like a terrible question to ask but I have been researching as much as I could to figure this out. I have an application that requires a view pager to scroll horizontally to display different views. Within each view, it needs functionality, for one view it could be just pressing a button, another view is required to download data from a server (for example, retrieving the latest Twitter feed) as well as other functionality. The main point is that within the View Pager, each view requires functionality.
My original idea was to follow this tutorial:
http://mobile.tutsplus.com/tutorials/android/android-user-interface-design-horizontal-view-paging/
However, this is just providing views which have no interaction. I have managed to implement this and add my layouts, however this is only solving half of the problem. It shows how to add basic operations within a comment such as a single button. I want each view to have its own activity which is capable of doing its own unique thing.
Here is what I originally had:
public class DashboardContainerActivity extends Activity{
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
//Set content view to the dashboard container xml view
setContentView(R.layout.dashboard_container);
//Create a new instance of my page adapter from below
MyPageAdapter adapter = new MyPageAdapter();
//Reference the view pager used in the dashboard container xml view
ViewPager myPager = (ViewPager) findViewById(R.id.dashboardpanelpager);
//set an adapter to the view pager
myPager.setAdapter(adapter);
//First panel to be shown when opened
myPager.setCurrentItem(3);
}
}
/*------------------------------------------------------*/
class MyPageAdapter extends PagerAdapter {
public int getCount() {
//Return 7 as there will be 7 panes in the dashboard
return 7;
}
public Object instantiateItem(View collection, int position) {
LayoutInflater inflater = (LayoutInflater) collection.getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = null;
switch (position) {
case 0:
v = inflater.inflate(R.layout.dashboard_social, null);
break;
case 1:
v = inflater.inflate(R.layout.dashboard_info, null);
//DISPLAY INFORMATION FROM SERVER AND DISPLAY HERE
break;
case 2:
v = inflater.inflate(R.layout.dashboard_allcourses, null);
//LIST OF COURSES
break;
case 3:
v = inflater.inflate(R.layout.dashboard_news, null);
//USE HTTP HERE FOR TWITTER FEED
break;
case 4:
v = inflater.inflate(R.layout.dashboard_mycourse, null);
//DOWNLOAD USER'S PERSONAL INFORMATION AND DISPLAY HERE
break;
case 5:
v = inflater.inflate(R.layout.dashboard_media, null);
//DISPLAY LATEST UPLOADED MULTIMEDIA
break;
case 6:
v = inflater.inflate(R.layout.dashboard_extras, null);
break;
}
((ViewPager) collection).addView(v, 0);
return v;
}
#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;
}
}
I have separate activities for each layout added to the ViewPager but I am guessing that these activities cannot be added into the ViewPager instead of just the layouts.
I have read something about fragments, but I am not sure whether that's compatible with API level 8, apparently this is targeted at Honeycomb and Ice Cream Sandwich.
It's not possible to have an Activity as part of a ViewPager, however there is no reason why you can't add the functionality you describe to each page in your ViewPager. To assign interaction or events to components in each view just add the correct listeners in instantiateItem() in each case statement:
case 0:
v = inflater.inflate(R.layout.dashboard_social, null);
Button myButton = (Button) v.findViewById(R.id.name_of_button_in_social_dashboard);
myButton.setOnClickListener(...);
break;
For any other interactions, like getting the http requests for the twitter feed, just execute those as part of your main activity (something like http requests should be done in a background thread of course). When you want to update the UI in the twitter page, just use ViewPager.getChildAt(3) to fetch the child element. Think of your Activity as just a big layout with 7 children views that are all available at once (but the user will only see them as they swipe).
With all that said, a better design patter might be to use Fragments with a FragmentPagerAdapter backing your ViewPager. This allows better logical breakdown of the various pages into different classes - Fragments also provide other uses like being able to load multiple on-screen at once for larger screen layouts (tablets).
Like ViewPager, Fragments are available all the way back to API Level 4 via the support library (see Fragment). So you don't need to worry about backward compatibility.
Now i can create PagerIndicator but i don't know how to put map it, which one page will show Driving Stat. and another one will show map+driving stat. In addition, Is it possible to make 2 view work together as you see in the picture below.(the time and distance still run)
I don't know about GMap control but here is how I use different layouts in my app.
First you could create a layout for your Map control
Secondly add it in your ViewPager
#Override
public Object instantiateItem(View pager, int position) {
LayoutInflater inflater = (LayoutInflater) SettingsActivity.this
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = null;
switch (position) {
case 0: //first page
view = inflater.inflate(R.layout.yourMapLayout,
null);
TextView text = (TextView) view
.findViewById(R.id.text);
text.setText("I am an added text")
...
If I understood corect and you are using android.support.v4.view.ViewPager , which would be a good candidate to accomplish your task.
I use below code and it works :)
#Override
public Object instantiateItem( View pager, final int position )
{
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = null;
if(position == 0) {
view = inflater.inflate(R.layout.my_map, null);
((ViewPager) pager).addView(view,0);
}
return view;
}
I have a ListView on my ListActivity and I'd like the rows of the ListView to be 1 of 3 different layouts. The first item in my list is always going to use layout A, the second item in my list is always going to use layout B, and all subsequent items are going to use layout C.
Here is my getView function:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// get the View for this list item
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
switch (position) {
case 0:
v = vi.inflate(R.layout.layout_A, parent, false);
break;
case 1:
v = vi.inflate(R.layout.layout_B, parent, false);
break;
default:
v = vi.inflate(R.layout.layout_C, parent, false);
break;
}
}
switch (position) {
case 0:
TextView txtLabel1 = (TextView)findViewById(R.id.label1);
TextView txtLabel2 = (TextView)findViewById(R.id.label2);
if (txtLabel1 != null) {
txtLabel1.setText("sdfasd");
}
if (txtLabel2 != null) {
txtLabel2.setText("dasgfadsasd");
}
break;
default:
break;
}
// return the created view
return v;
}
R.id.label1 and R.id.label2 are TextViews on R.layout.layout_A. However, txtLabel1 and txtLabel2 are null after trying to set them. Why?
I stepped through this code in the debugger and it inflated the correct layout (R.layout.layout_A) and fell into the correct case below to set the R.id.label1 and R.id.label2 text.
Also, if there is a better way to do this, please let me know.
Looks like "View v = convertView; if (v == null) {..." is a problem. You should re-create view every time because you don't know the type of given view. Also, you can use viewholder approach for more efficient implementation. You can find some ideas in this blog: http://codinglines.frankiv.me/post/18486197303/android-multiple-layouts-in-dynamically-loading