I have dynamically created tabs, in which I then programatically add a GridView with dragndrop controller. It all works if its only one tab, but if you scroll to the second one, rearrange items and then back, it crashes. I believe its because both tabs are using the same mDragController. It should be fairly easy to fix, any Ideas how to make them use separate controllers?
private DragController mDragController;
private boolean mLongClickStartsDrag = true; // If true, it takes a long click
protected void onCreate(Bundle savedInstanceState) {
...
final TabHost Tabs = (TabHost) findViewById(android.R.id.tabhost);
Tabs.setup();
int count;
for (count =0;count < 2;count++){
...
final int passedTabId = count;
NewTab.setContent(new TabHost.TabContentFactory()
{
public View createTabContent(String tag)
{
...
GridView dynGrid = new GridView(ManageRooms.this);
...
mDragController = new DragController (ManageRooms.this);
dynGrid.setAdapter (new ImageCellAdapter (ManageRooms.this, mDragController));
layout.addView(dynGrid);
return layout;
}
});
Tabs.addTab(NewTab);
}
}
public boolean startDrag (View v) {
v.setOnDragListener (mDragController);
mDragController.startDrag (v);
return true;
}
}
Solution: making an array of mDragControllers;
private DragController[] mDragController = new DragController[3];
and then accessing each one according to the id of the current Tab.
Related
I'm currently developping my first Android Application. Now I'm at the point where I really don't know how to handle the problem on my own. My Main Layout implements a ViewPager:
<android.support.v4.view.ViewPager
android:id="#+id/image_swipe"
android:layout_width="match_parent"
android:layout_height="590dp"
android:layout_marginTop="130dp" />
The Main Layout also implements a toolbar which owns this menu item:
<item
android:id="#+id/show_favorites"
android:orderInCategory="100"
android:icon="#drawable/heart_red"
android:title="#string/show_favorites" />
What I basically want to do, is: when the user clicks the menu item with id #show_favorites, the Adapter for the ViewPager should be filtered. Every item presented in the ViewPager has an ImageButton (favouriteButton). The button can be grey or red. The button state is stored as SharedPreferences, means: every item has its own key fav1 - fav10 (10 items) and this key has value 0 (grey) or value 1 (red). So when someone clicks the menu item, I only want to show items in the ViewPager which are marked as favorite (so in SharedPreferences it is stored 1). All items with a 0 have to be hidden.
My Adapter Class looks like you can see below:
public class SwipeAdapter extends PagerAdapter {
/*
Define the image resources for our image fragment
*/
private int[] image_resources = {
R.drawable.island_maldives,
R.drawable.island_kreta,
R.drawable.island_australia,
R.drawable.island_kosamui,
R.drawable.island_domrep,
R.drawable.island_bahamas,
R.drawable.island_mallorca,
R.drawable.island_philippines,
R.drawable.island_seychelles,
R.drawable.island_borabora
};
private Context context;
private LayoutInflater layoutInflater;
public SwipeAdapter(Context context) {
this.context = context;
}
/*
Count the images in image_resources array
*/
#Override
public int getCount() {
return image_resources.length;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return (view == (LinearLayout) object);
}
/*
Create the page for the given position
Set image resources and text about current position
*/
#Override
public Object instantiateItem(ViewGroup container, int position) {
layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View item_view = layoutInflater.inflate(R.layout.swipe_layout,container,false);
ImageView imageView = (ImageView) item_view.findViewById(R.id.swipe_image);
TextView textView = (TextView) item_view.findViewById(R.id.image_count);
ImageButton imageButton = (ImageButton) item_view.findViewById(R.id.favourite_button);
imageView.setImageResource(image_resources[position]);
// set tag for details
imageView.setTag(position);
textView.setText("["+(position+1)+"/10]");
SharedPreferences prefs = context.getSharedPreferences("favInfo", Context.MODE_PRIVATE);
// get the unique key consisting of fav and the current item position
String key = "fav" + (position+1);
imageButton.setTag(position+1);
String value = prefs.getString(key, "0");
// check if value of favorite key is 0
// set image resource dependent on what value is set
if (value.equals("1")) {
imageButton.setImageResource(R.drawable.heart_red);
} else {
imageButton.setImageResource(R.drawable.heart);
}
container.addView(item_view);
return item_view;
}
/*
Remove a page for the given position
*/
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((LinearLayout) object);
}
}
And my Main Activity is organized this way:
public class SuggestionActivity extends AppCompatActivity {
ViewPager viewPager;
SwipeAdapter adapter;
private final AppCompatActivity activity = SuggestionActivity.this;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_suggestion);
viewPager = (ViewPager) findViewById(R.id.image_swipe);
adapter = new SwipeAdapter(this);
viewPager.setAdapter(adapter);
// set the toolbar for the suggestion page
Toolbar sToolbar = (Toolbar) findViewById(R.id.suggestion_toolbar);
setSupportActionBar(sToolbar);
};
/*
Add options menu at view start
*/
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.suggestion_menu, menu);
return super.onCreateOptionsMenu(menu);
}
/*
Declare what happens when user clicks an item of the options menu
#go_home start the CategoryActivity
#filter TO DO
*/
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.go_home:
Intent homeIntent = new Intent(activity, CategoryActivity.class);
startActivity(homeIntent);
return true;
case R.id.show_favorites:
Intent favIntent = new Intent(activity, SuggestionActivity.class);
//
// HERE IS WHERE I DON'T KNOW WHAT TO DO
//
startActivity(favIntent);
return true;
default:
// If we got here, the user's action was not recognized.
// Invoke the superclass to handle it.
return super.onOptionsItemSelected(item);
}
}
/*
Method for click actions on the favorite button
#heart grey heart symbolizes no favorite (flag 0)
#heart_red red heart symbolizes marked as favorite (flag 1)
*/
public void onClickFav(View v) {
ImageButton favouriteButton = (ImageButton) v;
SharedPreferences prefs = getSharedPreferences("favInfo", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
// get the unique key by getting the tag of the image button
String c = v.getTag().toString();
String key = "fav"+c;
// get value stored with that key, default: if no value exists take 0
String value = prefs.getString(key, "0");
// check if value of favorite key is 0,
// set image resource dependent on what value is stored behind the key
if (value.equals("0")) {
favouriteButton.setImageResource(R.drawable.heart_red);
} else {
favouriteButton.setImageResource(R.drawable.heart);
}
// do some magic text dependent on what value is set
if (value.equals("1")) {
Toast.makeText(SuggestionActivity.this, "No favourite anymore...",
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(SuggestionActivity.this, "Woah, new favourite!",
Toast.LENGTH_SHORT).show();
}
String isFav;
// switch value to opposite
if (value.equals("0")) {
isFav = "1";
} else {
isFav = "0";
}
// store shared preferences
editor.putString(key, isFav);
editor.commit();
}
}
In sum: The ImageButton state of each item in the ViewPager is already stored as SharedPreferenecs in form of tags 0/1. After clicking the menu item with id #show_favorites, I only want to show the items with 1 as stored value.
How can I do this? Thank you for your advice!
I'm fairly new to Android programming, and I'm trying to design an app where when I swipe through several images on a ViewPager that takes up some of the screen, other elements of that same activity/screen will change with it without necessarily 'scrolling' horizontally like the ViewPager does. My problem is that I can't find a way to identify which image in the ViewPager is the 'current' one outside of the ViewPager.
I tried to create a getter for grabbing the position from instantiateItem method, but no luck - because I assume it simply creates everything once, not updating it again, so when I swipe nothing will happen. Also, I realize that my dieValue variable doesn't do anything - but it's meant to serve as an example of what I want to accomplish. The dieValue variable would change based on which image was the current one.
CustomSwipeAdapter.java
public class CustomSwipeAdapter extends PagerAdapter {
private Context ctx;
public int[] image_resources = {R.drawable.d20, R.drawable.d8, R.drawable.d6};
public CustomSwipeAdapter(Context ctx) {
this.ctx = ctx;
}
#Override
public int getCount() {
return image_resources.length;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
LayoutInflater layoutInflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View item_view = layoutInflater.inflate(R.layout.swipe_layout, container, false);
ImageView imageView = (ImageView) item_view.findViewById(R.id.dieImageView);
imageView.setImageResource(image_resources[position]);
TextView textView = (TextView) item_view.findViewById(R.id.dieValueTop);
if (position == 0) { textView.setText(R.string.d20Label); }
if (position == 1) { textView.setText(R.string.d8Label); }
if (position == 2) { textView.setText(R.string.d6Label); }
container.addView(item_view);
return item_view;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((LinearLayout) object);
}
}
DiceRollerActivity.java
public class DiceRollerActivity extends Activity implements View.OnClickListener {
ViewPager viewPager;
CustomSwipeAdapter adapter;
private int dieValue;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dice_roller);
viewPager = (ViewPager) findViewById(R.id.dieViewPager);
adapter = new CustomSwipeAdapter(this);
viewPager.setAdapter(adapter);
Button RollDieButton = (Button) findViewById(R.id.rollDieButton);
RollDieButton.setOnClickListener(this);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.rollDieButton:
Random rand = new Random();
int random = rand.nextInt(dieValue) + 1;
setDieResults(random);
}
}
// Prints the results of the die roll to the results log
private void setDieResults(int random) {
TextView dieResults = (TextView) findViewById(R.id.dieResultsLabel);
if (random == dieValue) {
dieResults.setText(getString(R.string.YouRolledLabel) + random + getString(R.string.criticalHit));
} else if (random == dieValue) {
dieResults.setText(getString(R.string.YouRolledLabel) + random + getString(R.string.criticalMiss));
}else{
dieResults.setText(getString(R.string.YouRolledLabel) + random);
}
}
}
Create a method inside your CustomSwipeAdapter
public int getCurrentImageResource(int currentPosition){
return image_resources[currentPosition];
}
To access to the current image call this method passing current position with viewPager.getCurrentItem() like this
adapter.getCurrentImageResource(viewPager.getCurrentItem());
Hope this helps!
You could use
viewPager.getCurrentItem();
on your DiceRollerActivity.class to get the current position.
Then, to get the image a better approach would be to have the image array on your DiceRollerActivity.class and pass it onto the CustomSwipeAdapter constructor.
Imagine there is a viewpager with 4 page, the 1st and 2nd page are storing the edittext, and the third one need to display the inputed data from 1st and 2nd page.
The problem is , viewpager pre-load the pervious page and next page , if I create the custom adapter like that:
Custom adapter
#Override
public Object instantiateItem(ViewGroup container, int position) {
rootView = (LinearLayout) LayoutInflater.from(ctx).inflate(pages[position], null);
if (position == 0) {
form1(rootView);
} else {
form2(rootView);
}
((ViewPager)container).addView(rootView);
return rootView;
}
Example function form2
private void form2(View rootView){
TextView previous_page = (TextView) rootView.findViewById(R.id.previous_page);
TextView next_page = (TextView) rootView.findViewById(R.id.next_page);
final EditText type = (EditText) rootView.findViewById(R.id.edit_type);
final EditText amount = (EditText) rootView.findViewById(R.id.edit_amount);
final Spinner period = (Spinner) rootView.findViewById(R.id.edit_period);
final EditText name = (EditText) rootView.findViewById(R.id.edit_name);
final EditText phone_no = (EditText) rootView.findViewById(R.id.edit_phone_no);
final EditText email = (EditText) rootView.findViewById(R.id.edit_email);
previous_page.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
viewPager.setCurrentItem(0, true);
top_bar.setImageResource(R.drawable.form_1_header);
}
});
next_page.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
String[] input_list = {amount.getText().toString(),name.getText().toString(),phone_no.getText().toString(),email.getText().toString()};
String invalid_msg = check_valid(input_list);
if (invalid_msg.equals("")) {
new FormHandler(ctx,formListener).execute(type.getText().toString(),amount.getText().toString(),period.getSelectedItem().toString(),name.getText().toString(),phone_no.getText().toString(),email.getText().toString());
} else {
Toast.makeText(ctx, invalid_msg ,Toast.LENGTH_LONG).show();
}
}
});
}
Then , for example if I enter the 1st page, it call the instantiateItem 2 times, the position are 0 and 1 , and it called the form1() and the form2(), which I expect only when I enter that page , it call the function of that page . e.g. At 1st page , run form1(), At 2nd page , run form2(). How to fix that? thanks.
Update (1):
It caused a problem, when I enter 2nd tab , it preload the 3rd tab, which call the form3(), so after I input the data in those edittext at 2nd tab, and go to the 3rd tab, it does not call form3() again, so the 3rd tab does not display enter data from 2nd tab (The view was preload and instantiate already)
Update (2):
The page is not a fragment , it is a layout and inflate at the adapter(named "rootview" and the pages array is:)
int[] pages = {R.layout.form_1,R.layout.form_2,R.layout.form_3,R.layout.form_4};
Update (3):
My whole viewpager
private class ViewPageAdapter extends PagerAdapter {
// Declare Variables
public int[] pages;
private LinearLayout rootView;
public ViewPageAdapter(int[] _pages) {
pages = _pages;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == (LinearLayout) object;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
rootView = (LinearLayout) LayoutInflater.from(ctx).inflate(
pages[position], null);
if (position == 0) {
form1(rootView);
} else if (position == 1) {
form2(rootView);
} else if (position == 2) {
form3(rootView);
} else if (position == 3) {
form4(rootView);
}
((ViewPager) container).addView(rootView);
return rootView;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
((ViewPager) container).removeView((LinearLayout) object);
}
#Override
public int getCount() {
return pages.length;
}
}
You can override the setPrimaryItem method in adapter. this method will give the current displaying object. This may help you.
#Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
if (mobject != object) {
mObject=objet; //mObject is global variable in adapter
//You can update your based on your logic like below
View view == (LinearLayout) object;
form4(view);
}
super.setPrimaryItem(container, position, object);
}
Then , for example if I enter the 1st page, it call the instantiateItem 2 times, the position are 0 and 1 , and it called the form1() and the form2(), which I expect only when I enter that page , it call the function of that page . e.g. At 1st page , run form1(), At 2nd page , run form2(). How to fix that? thanks.
you can not fix that, because of giving better UX to user setOffscreenPageLimit(0); dose not work and the default value is always 1 which means it always preloads your next and previous page.
so after I input the data in those edittext at 2nd tab, and go to the
3rd tab, it does not call form3() again, so the 3rd tab does not
display enter data from 2nd tab (The view was preload and instantiate
already)
you can save your data in SharedPreferaces and read them in onResume method of tab3. in this way you will get correct data from tab2 and tab1.
I've got an android app, with a super-class that contains a layout that should be static for each activity, the illustration beneath shows this in a better way rather than my description
This "header" contains a tabBar which contains a ImageButton. I want this header to be static for all the activities in my app. What I tried to do, is to extend my other classes from this superclass. Code for the super class is beneath
public class MySuperClass extends Activity {
MyHorizontalScrollView scrollView;
View menu;
View app;
ImageButton btnSlide;
boolean menuOut = false;
Handler handler = new Handler();
int btnWidth;
Button testClass;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LayoutInflater inflater = LayoutInflater.from(this);
scrollView = (MyHorizontalScrollView) inflater.inflate(R.layout.horz_scroll_with_list_menu, null);
setContentView(scrollView);
menu = inflater.inflate(R.layout.horz_scroll_menu, null);
app = inflater.inflate(R.layout.horz_scroll_app, null);
ViewGroup tabBar = (ViewGroup) app.findViewById(R.id.tabBar);
ListView listView = (ListView) app.findViewById(R.id.list);
listView = (ListView) menu.findViewById(R.id.list);
ArrayList<MenuItem> menuItems = getMenuItems();
listView.setAdapter(new MenuCustomAdapter(this, menuItems));
btnSlide = (ImageButton) tabBar.findViewById(R.id.BtnSlide);
btnSlide.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
btnSlide.setImageResource(R.drawable.lincolor);
break;
case MotionEvent.ACTION_UP:
btnSlide.setImageResource(R.drawable.lin);
break;
}
return false;
}
});
btnSlide.setOnClickListener(new ClickListenerForScrolling(scrollView, menu));
testClass = (Button) app.findViewById(R.id.button1);
testClass.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
Intent intent = new Intent(MySuperClass.this, TestClass.class);
startActivity(intent);
}
});
final View[] children = new View[] { menu, app };
// Scroll to app (view[1]) when layout finished.
int scrollToViewIdx = 1;
scrollView.initViews(children, scrollToViewIdx, new SizeCallbackForMenu(btnSlide));
}
public ArrayList<MenuItem> getMenuItems() {
ArrayList<MenuItem> items = new ArrayList<MenuItem>();
MenuItem m1 = new MenuItem(R.drawable.scroll, "Show history");
items.add(m1);
MenuItem m2 = new MenuItem(R.drawable.right, "Right");
items.add(m2);
return items;
}
/**
* Helper for examples with a HSV that should be scrolled by a menu View's width.
*/
static class ClickListenerForScrolling implements OnClickListener {
HorizontalScrollView scrollView;
View menu;
ImageButton button;
int pressed;
int timeout;
/**
* Menu must NOT be out/shown to start with.
*/
boolean menuOut = false;
public ClickListenerForScrolling(HorizontalScrollView scrollView, View menu) {
super();
this.scrollView = scrollView;
this.menu = menu;
}
#Override
public void onClick(View v) {
Context context = menu.getContext();
int menuWidth = menu.getMeasuredWidth();
// Ensure menu is visible
menu.setVisibility(View.VISIBLE);
if (!menuOut) {
// Scroll to 0 to reveal menu
int left = 0;
scrollView.smoothScrollTo(left, 0);
} else {
// Scroll to menuWidth so menu isn't on screen.
int left = menuWidth;
scrollView.smoothScrollTo(left, 0);
}
menuOut = !menuOut;
}
}
/**
* Helper that remembers the width of the 'slide' button, so that the 'slide' button remains in view, even when the menu is
* showing.
*/
static class SizeCallbackForMenu implements SizeCallback {
int btnWidth;
View btnSlide;
public SizeCallbackForMenu(View btnSlide) {
super();
this.btnSlide = btnSlide;
}
#Override
public void onGlobalLayout() {
btnWidth = btnSlide.getMeasuredWidth();
System.out.println("btnWidth=" + btnWidth);
}
#Override
public void getViewSize(int idx, int w, int h, int[] dims) {
dims[0] = w;
dims[1] = h;
final int menuIdx = 0;
if (idx == menuIdx) {
dims[0] = w - btnWidth;
}
}
}
}
And a test class, which extends this superclass.
public class TestClass extends MySuperClass {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
}
}
Again how can I make the tabBar static for each activity?
There is no way to achieve this in an Activity-scope. The layout are independent from your Acitvity (well, until you bind them).
The solution to your problem, may be in using a common header layout, kept in a xml file under your layout folder, something like this:
header.xml:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/red"
... />
And include them in your layouts, using the include tag:
my_activity_layout.xml:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
....
.... >
<include layout="#layout/header.xml" android:id="#+id/header" />
<!-- Countinue your layout .... -->
</RelativeLayout>
you can use a TabHost right. by click on each tab you can launch separate activity
http://mfarhan133.wordpress.com/2010/11/17/tablayouttabhost-tutorial-for-android-reusing-layout/
I don't know if that is possible. I will post an alternative solution, which requires a little bit of more code but works, and is a best practice, so if nothing comes up this is the best alternative, probably the only one.
When you are trying to use a certain layout again and again, like the tab bar that you are mentioning, there is the <merge> and <include> functionality in the layout. The basic idea is that you make a layout.xml file that you want to include in other layouts.
This post gives a very good example of how to use it. Simple example of <merge> and <include> usage in Android XML-layouts
How do you unlock the ability to slide to a new page after clicking a button on a previous page?
Currently I have a PagerAdapter. The code below instantiates the item. Within the getCount(), a value of 3 is returned and thus 3 slides are made. I was wondering if there was a way to only have 3 views, but after clicking a button, unlock a 4th view that you can now slide to?
#Override
public Object instantiateItem(final View collection, int position) {
final LayoutInflater inflater = (LayoutInflater) TViewPager.this
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
TextView tv = new TextView(TViewPager.this);
//Describe the separate layouts of views
switch (position) {
case 1:
View v1 = inflater.inflate(R.layout.expander2, null, false);
((ViewPager) collection).addView(v1, 0);
final Button button = (Button) findViewById(R.id.button_click_me);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v1) {
Toast.makeText(getBaseContext(), "+1", Toast.LENGTH_LONG).show();
//*Some code*//
}
});
return v1;
Is there a way to change the:
public int getCount() {
return 3;
}
Like a "setCount()" and then place it at the //* some code *// to increase the amount of slides?
Would adding
instantiateItem(collection,4);
work somewhere work?
Thanks,
Here's how I solved this problem. To clarify, how do you click a button and get more Pages that you can scroll into. I made:
private int NUM_VIEWS = 2;
public void setN(int N) {
this.NUM_VIEWS = N;
}
and then I changed an important line.
#Override
public int getCount() {
return NUM_VIEWS;
}
My clickListener is
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v1) {
myAdapter.setN(3);
myPager.setCurrentItem(2);
}
});
I added another case to facilitate the new item. Afterwards when I click the button, my pageadapter will expand to 3 views instead of 2 and the new case will be the new view.
getCount by default returns the number of child views of your ViewPager. By doing pager.add(layout_item) you can add a new item to the pager (of course you have to inflate it somewhere first). getCount will automatically adjust.