I am developing a small application consisting of several tabs where each tab holds just one kind of products (for example pizza products) and makes it possible to add specific product (Margherita) from this tab to an order. Each tab holds layout where there are buttons representing each product on the left side and list of items (all products) currently added to the order + calculated prices on the right side. This is the way how I currently add Tab in TabHost:
Bundle extras = new Bundle();
extras.putString("productType", "pizza");
tabHost.addTab(tabHost.newTabSpec("pizza").setIndicator("Pizza"), OrderFragment.class, extras);
The problem is that when I try to get arguments in a constructor of OrderFragment, it throws an exception:
E/AndroidRuntime(533): FATAL EXCEPTION: main
E/AndroidRuntime(533): java.lang.NullPointerException
The reason why I need to pass some data to the OrderFragment is that OrderFragment is GOING to be an abstract class for any kind of product (product, pasta, etc.), so it can find out, for which type of product it is and load proper data for that. I thought that the method addTab(TabSpec, Class<T>, Bundle) creates an instance of specified class and puts extras in it but it seems like that these extras are not packed with it. (Guess it is for another purpose). I would appreciate any suggestions how to solve this. If you would do it in completely different way, I would also appreciate pointing it out.
OrderFragment:
public class OrderFragment extends Fragment implements IEditOrder{
private static final String TAG = "OrderFragment";
private Controller controller;
private ListView lst_order;
private TextView txt_order_total;
private Button btn_finishOrder;
private Button btn_cancelOrder;
private String productType;
private OrderAdapter orderAdapter;
private static final int DIALOG_EDIT_ORDER = 1;
public OrderFragment() {
Bundle extras = getArguments();
Log.d(TAG, "Extras: " + extras.toString());
this.productType = extras.getString("productType");
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//setRetainInstance(true);
controller = Controller.getInstance();
controller.loadProducts(getActivity().getApplicationContext());
//FillDatabase.loadDataToDatabase(getActivity());
}
#Override
public void onSaveInstanceState(Bundle outState) {
// TODO Auto-generated method stub
super.onSaveInstanceState(outState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_pizza, container, false);
txt_order_total = (TextView)view.findViewById(R.id.txt_order_total);
btn_finishOrder = (Button)view.findViewById(R.id.btn_finishOrder);
btn_cancelOrder = (Button)view.findViewById(R.id.btn_cancelOrder);
btn_finishOrder.setOnClickListener(finishOrder);
btn_cancelOrder.setOnClickListener(cancelOrder);
final RelativeLayout layout = (RelativeLayout) view.findViewById(R.id.rl_order_pizza);
orderAdapter = new OrderAdapter(getActivity(), controller.getOrderMap());
lst_order = (ListView) view.findViewById(R.id.lst_order);
lst_order.addHeaderView(inflater.inflate(R.layout.order_list_header, null));
lst_order.setAdapter(orderAdapter);
lst_order.setOnItemClickListener(itemClickListener);
txt_order_total.setText("Total Price: " + controller.calculateTotalPrice());
layout.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
layout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
RelativeLayout.LayoutParams layoutParams;
int currentX = 20;
int currentY = 20;
for (Product product: controller.getProducts("pizza")){
layoutParams = new RelativeLayout.LayoutParams(UIConstnts.BUTTON_WIDTH, UIConstnts.BUTTON_HEIGHT);
Button tempButton = new Button(getActivity().getApplicationContext());
tempButton.setId((int)product.getId());
tempButton.setText(product.getName());
tempButton.setOnClickListener(clickListener);
layoutParams.setMargins(currentX, currentY, 0, 0);
tempButton.setLayoutParams(layoutParams);
layout.addView(tempButton);
if (layout.getWidth() < currentX + UIConstnts.MARGIN_LEFT + (2 * UIConstnts.BUTTON_WIDTH)){
currentX = 20;
currentY += UIConstnts.BUTTON_HEIGHT + UIConstnts.MARGIN_BOTTOM;
}
else{
currentX += UIConstnts.MARGIN_LEFT + UIConstnts.BUTTON_WIDTH;
}
}
layout.requestLayout();
}
});
return view;
}
private OnClickListener clickListener = new OnClickListener(){
#Override
public void onClick(View view) {
controller.addOrderItem(1, (long)view.getId());
updateQuantity();
txt_order_total.setText("Total Price: " + controller.calculateTotalPrice());
}
};
private OnItemClickListener itemClickListener = new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
showDialog(id, Integer.valueOf(((TextView)view.findViewById(R.id.txt_lst_quantity)).getText().toString()));
}
};
private OnClickListener finishOrder = new OnClickListener(){
#Override
public void onClick(View v) {
controller.finishOrder(getActivity());
updateQuantity();
}
};
private OnClickListener cancelOrder = new OnClickListener(){
#Override
public void onClick(View v) {
controller.resetOrder();
updateQuantity();
}
};
private void showDialog(long productId, int quantity) {
DialogFragment newFragment = EditOrderDialog.newInstance(productId, quantity, "Change Quantity");
newFragment.setTargetFragment(this, DIALOG_EDIT_ORDER);
newFragment.show(getFragmentManager(), "dialog");
}
#Override
public void updateQuantity() {
orderAdapter.setOrderList(controller.getOrderMap());
orderAdapter.notifyDataSetChanged();
txt_order_total.setText("Total Price: " + controller.calculateTotalPrice());
}
}
Picture of UI. (ListView on the right side is present on all the tabs and contains all the products in the order):
Don't ask for the arguments/extras in the constructor! They are not set at this time. They will be available later.
//If you create your Fragments yourself, create them in a static factory method:
public static Fragment newInstance(String productType){
Bundle b = new Bundle();
b.putString(ARG_PRODUCT_TYPE, productType);
Fragment f = new OrderFragment();
f.setArguments(b);
return f;
}
//Write a helper method for your arguments
public String getProductType(){
if(getArguments().containsKey(ARG_PRODUCT_TYPE)){
return getArguments().getString(ARG_PRODUCT_TYPE);
}
return null;
}
Then use the helper method after the Constructor, i.e. in onCreate().
Related
I have a landscape configuration for one of my app's activities. This activity contains a fragment and this fragment contains one textview and one recyclerview. Everytime when i switch in between portrait and landscape modes, the recyclerview leaves the view of itself like however it was before i turned the device. It might be a little difficult to understand what i try to ask here, so i recorded a gif for that.
https://giphy.com/gifs/3Wv7NAtT8ezP1SQhDu
This is my activity
public class RecipeStepsActivity extends AppCompatActivity {
static Recipe recipe;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recipe_steps);
if (StepDetailActivity.SDA_TAG.equals(StepDetailActivity.NEGATIVE))
recipe = getIntent().getParcelableExtra("recipe");
Bundle b = new Bundle();
b.putParcelable("recipe", recipe);
ActionBar ab = getSupportActionBar();
if (ab != null)
ab.setTitle(recipe.getName());
RecipeStepsFragment recipeStepsFragment = new RecipeStepsFragment();
recipeStepsFragment.setArguments(b);
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction().add(R.id.frame_layout_steps, recipeStepsFragment).commit();
}
}
This is my fragment
public class RecipeStepsFragment extends Fragment {
#BindView(R.id.recipe_steps_rv)
RecyclerView recyclerView;
#BindView(R.id.ingredients_tv)
TextView tv_ingredients;
List<Step> steps;
public RecipeStepsFragment(){}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_recipe_steps, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
List<Ingredients> ingredients;
Recipe recipe = getArguments().getParcelable("recipe");
steps = recipe.getSteps();
initRecyclerView();
ingredients = recipe.getIngredients();
String ingredientsAppended = "INGREDIENTS" + "\n\n";
if (ingredients == null){
ingredientsAppended = "Not Available";
} else {
for (int a = 0 ; a < ingredients.size() ; a++) {
Ingredients i = ingredients.get(a);
ingredientsAppended += String.valueOf(i.getQuantity()) + " " +
i.getMeasure() + " " +
i.getIngredient();
if (a+1 != ingredients.size()){
ingredientsAppended += '\n';
}
}
}
tv_ingredients.setText(ingredientsAppended);
if(savedInstanceState != null){
recyclerView.scrollToPosition(savedInstanceState.getInt("position"));
}
}
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
outState.putInt("position", recyclerView.getVerticalScrollbarPosition());
super.onSaveInstanceState(outState);
}
private void initRecyclerView(){
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL));
RecipeStepsRecyclerAdapter recipeStepsRecyclerAdapter =
new RecipeStepsRecyclerAdapter(steps, new RecipeStepsRecyclerAdapter.ClickListener() {
#Override
public void onItemClick(int clickedItemPosition) {
Intent intentToStepDetail = new Intent(getActivity(), StepDetailActivity.class);
Step step = steps.get(clickedItemPosition);
intentToStepDetail.putExtra("step", step);
startActivity(intentToStepDetail);
}
}, getContext());
recyclerView.setAdapter(recipeStepsRecyclerAdapter);
recipeStepsRecyclerAdapter.notifyDataSetChanged();
}
}
This is my adapter
public class RecipeStepsRecyclerAdapter extends RecyclerView.Adapter<RecipeStepsRecyclerAdapter.ViewHolder> {
private List<Step> stepList;
private LayoutInflater mInflater;
final private ClickListener clickListener;
public RecipeStepsRecyclerAdapter(List<Step> stepList, ClickListener clickListener, Context context){
this.stepList = stepList;
this.clickListener = clickListener;
mInflater = LayoutInflater.from(context);
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.recipe_steps_recyclerview_adapter, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
Step step = stepList.get(position);
String stepContent = step.getShortDescription();
holder.listingNumber.setText(String.valueOf(position+1));
holder.stepContent.setText(stepContent);
}
#Override
public int getItemCount() {
return stepList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
TextView listingNumber;
TextView stepContent;
private ViewHolder(View itemView) {
super(itemView);
listingNumber = itemView.findViewById(R.id.list_number_tv);
stepContent = itemView.findViewById(R.id.step_content_tv);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
int clickedPosition = getAdapterPosition();
clickListener.onItemClick(clickedPosition);
}
}
public interface ClickListener{
void onItemClick(int clickedItemPosition);
}
}
Layout files are as I already explained above. I guess nothing special to post here.
Thanks in advance.
The problem is that you add a new fragment to the Activity every time it is created. Here's the end of your onCreate(...):
RecipeStepsFragment recipeStepsFragment = new RecipeStepsFragment();
recipeStepsFragment.setArguments(b);
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction().add(R.id.frame_layout_steps, recipeStepsFragment).commit();
The FragmentManager keeps a reference to the fragment you add to it even if the host Activity is destroyed. Thus, you keep adding new instances of the RecipeStepsFragment eventually overlaying each other and producing the seen behavior.
Don't worry, the fix is pretty simple: use replace(...) instead of add(...):
fm.beginTransaction().replace(R.id.frame_layout_steps, recipeStepsFragment).commit();
P.S.: Note, however, that replacing the current fragment with a new one every time the host activity is destroyed is not a good idea. You should check either if the savedInstanceState is null (which indicates that it's a non-recreated Activity) or if the given fragment is already added (define a tag when adding the fragment and try finding it in onCreate(...) before replacing the old one).
Every time you change the rotation of the screen your activity is destroyed and recreated. That is the reason why your list is populating with new items each time when you switch between your orientation.
So it's important you only instantiate your objects once, and not keep recreating them each time your app is recreated. Which then adds them to your list.
You can use onSaveInstanceState and onRestoreInstanceState.
If you want your app to allow orientation change, apply logic so your list don't get new items each time when activity is created.
Put configChanges attribute in your activity in menifest file
<activity android:name=".VideoActivity" android:configChanges="keyboardHidden|orientation|screenSize"/>
I'm new in android and want an app with viewpager. I am unable to design the viewpager in the layout. The layout fetch the data dynamatically. I want to create a matreial UI viewpager which swipes the page left or right like a paper.I have made a view which shows the data but is unable to swipe like a viewpager left or right...
The code is as below:
public class DetailNewsActivity extends AppCompatActivity {
private PullToZoomScrollViewEx scrollView;
private ArrayList<NewsItemModel> newsArr;
private TextView tvNewsTitle;
private TextView tvNewsPublishDate;
private TextView tvNewsFull;
private int newsPosition = 0;
private ImageView ivYoutubeEnable;
private SharedPreferences sharedPref;
int textSize = 16;
boolean isHighQuality = false;
private Typeface typeface;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail_news);
init();
AdView mAdView = (AdView) findViewById(R.id.adView);
AdRequest adRequest = new AdRequest.Builder()
.addTestDevice("5745FD6726ACCBEE8324DB158D021FA5")
.build();
mAdView.loadAd(adRequest);
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_detail_news, container, false);
return view;
}
private void init() {
typeface = Typeface.createFromAsset(getAssets(), "AnmolUni.ttf");
// newsArr = (ArrayList<NewsItemModel>) getIntent().getSerializableExtra("newsArray");
newsArr = AppController.getAppController().getMainNewsArr();
AppController.getAppController().setMainNewsArr(null);
newsPosition = getIntent().getIntExtra("newsPosition", 0);
findViewById(R.id.iv_back).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
finish();
}
});
findViewById(R.id.iv_share).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
generateBranchURL(newsPosition);
}
});
sharedPref = getSharedPreferences(Constants.SHARED_PREF_NAME, Context.MODE_PRIVATE);
textSize = sharedPref.getInt("text_size", 16);
isHighQuality = sharedPref.getBoolean("isHighQuality",false);
loadViewForCode();
scrollView = (PullToZoomScrollViewEx) findViewById(R.id.scroll_view);
setNewsFromArray(newsPosition);
// ((ImageView) scrollView.getZoomView().findViewById(R.id.iv_zoom)).setImageResource(android.R.drawable.arrow_down_float);
(scrollView.getZoomView().findViewById(R.id.iv_zoom)).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(DetailNewsActivity.this, ImageVideoActivity.class);
intent.putExtra("viewType", "image");
if(isHighQuality)
intent.putExtra("imageArr", newsArr.get(newsPosition).getImage());
else
intent.putExtra("imageArr", newsArr.get(newsPosition).getMedium());
startActivity(intent);
}
});
DisplayMetrics localDisplayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(localDisplayMetrics);
int mScreenHeight = localDisplayMetrics.heightPixels;
int mScreenWidth = localDisplayMetrics.widthPixels;
LinearLayout.LayoutParams localObject = new LinearLayout.LayoutParams(mScreenWidth, (int) (9.0F * (mScreenWidth / 16.0F)));
scrollView.setHeaderLayoutParams(localObject);
scrollView.setParallax(true);
// scrollView.getPullRootView().findViewById(R.id.container_layout).setOnTouchListener(new OnSwipeTouchListener(DetailNewsActivity.this) {
scrollView.getPullRootView().setOnTouchListener(new OnSwipeTouchListener(DetailNewsActivity.this) {
#Override
public void onSwipeLeft() {
if (newsPosition < newsArr.size() - 1) {
newsPosition++;
setNewsFromArray(newsPosition);
} else {
CommonUtils.showToast(DetailNewsActivity.this, "No more news");
}
}
#Override
public void onSwipeRight() {
if (newsPosition > 0) {
newsPosition--;
setNewsFromArray(newsPosition);
} else {
CommonUtils.showToast(DetailNewsActivity.this, "This is the first news");
}
}
});
scrollView.setOnPullZoomListener(new PullToZoomBase.OnPullZoomListener() {
#Override
public void onPullZooming(int newScrollValue) {
Log.d("MainActivity", "onPullZooming: " + newScrollValue);
// Intent intent = new Intent(DetailNewsActivity.this, ImageVideoActivity.class);
// intent.putExtra("viewType", "image");
// intent.putExtra("imageArr", newsArr.get(newsPosition).getImage());
// startActivity(intent);
}
#Override
public void onPullZoomEnd() {
}
});
}
void setNewsFromArray(int position) {
if (position >= newsArr.size()) {
finish();
return;
}
if (!newsArr.get(position).getMedium().get(0).isEmpty()) {
if(isHighQuality)
Picasso.with(DetailNewsActivity.this).load(newsArr.get(position).getImage().get(0)).placeholder(R.drawable.logo).error(R.drawable.logo).
into((ImageView) scrollView.getZoomView().findViewById(R.id.iv_zoom));
else
Picasso.with(DetailNewsActivity.this).load(newsArr.get(position).getMedium().get(0)).placeholder(R.drawable.logo).error(R.drawable.logo).
into((ImageView) scrollView.getZoomView().findViewById(R.id.iv_zoom));
} else {
((ImageView) scrollView.getZoomView().findViewById(R.id.iv_zoom)).setImageResource(R.drawable.logo);
}
tvNewsTitle.setText(newsArr.get(position).getTitle());
tvNewsPublishDate.setText(newsArr.get(position).getPublish_dt());
//String style = "<html><body style='text-align:justify'>";
//Log.i("RAJEEV",style + newsArr.get(position).getFullnews());
tvNewsFull.setText(Html.fromHtml( newsArr.get(position).getFullnews()));
if (newsArr.get(position).getYoutube_video().isEmpty()) {
ivYoutubeEnable.setVisibility(View.GONE);
} else {
ivYoutubeEnable.setVisibility(View.VISIBLE);
}
ivYoutubeEnable.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(DetailNewsActivity.this, ImageVideoActivity.class);
intent.putExtra("viewType", "youtube");
intent.putExtra("youtube_code", newsArr.get(newsPosition).getYoutube_video());
startActivity(intent);
}
});
}
private void loadViewForCode() {
PullToZoomScrollViewEx scrollView = (PullToZoomScrollViewEx) findViewById(R.id.scroll_view);
View headView = LayoutInflater.from(this).inflate(R.layout.ptz_head_view, null, false);
View zoomView = LayoutInflater.from(this).inflate(R.layout.ptz_zoom_view, null, false);
View contentView = LayoutInflater.from(this).inflate(R.layout.ptz_content_view, null, false);
scrollView.setHeaderView(headView);
scrollView.setZoomView(zoomView);
scrollView.setScrollContentView(contentView);
tvNewsTitle = (TextView) scrollView.getPullRootView().findViewById(R.id.tv_news_title);
tvNewsPublishDate = (TextView) scrollView.getPullRootView().findViewById(R.id.tv_news_publish_date);
tvNewsFull = (TextView) scrollView.getPullRootView().findViewById(R.id.tv_news_full);
tvNewsFull.setTextSize(textSize);
if (!AppController.isPunjabiSupported()) {
tvNewsTitle.setTypeface(typeface);
tvNewsFull.setTypeface(typeface);
}
ivYoutubeEnable = (ImageView) scrollView.getHeaderView().findViewById(R.id.iv_youtube_header);
}
void generateBranchURL(final int newsPosition) {
// String imageUrl = "";
// if (newsArr.get(newsPosition).getMedium().size() <= 1)
// imageUrl = newsArr.get(newsPosition).getMedium().get(0);
BranchUniversalObject branchUniversalObject = new BranchUniversalObject()
.setCanonicalIdentifier("NewsDetails")
///.setCanonicalUrl("https://branch.io/deepviews")
//.setTitle("" + newsArr.get(newsPosition).getTitle())
//.setContentDescription("" + newsArr.get(newsPosition).getIntro())
//.setContentImageUrl("" + imageUrl)
// You use this to specify whether this content can be discovered publicly - default is public
.setContentIndexingMode(BranchUniversalObject.CONTENT_INDEX_MODE.PUBLIC);
// Here is where you can add custom keys/values to the deep link data
//.addContentMetadata("property1", "blue")
//.addContentMetadata("property2", "red");
LinkProperties linkProperties = new LinkProperties()
.addControlParameter("$desktop_url", "http://www.newsnumber.com/news/share/" + newsArr.get(newsPosition).getFn_id())
.addControlParameter("$ios_url", "itms-apps://itunes.apple.com/us/app/newsnumber/id1022442357?mt=8")
.addControlParameter("NewsId", "" + newsArr.get(newsPosition).getFn_id())
.addControlParameter("CatId", "" + newsArr.get(newsPosition).getCat_id())
.addControlParameter("$og_title", newsArr.get(newsPosition).getTitle())
.addControlParameter("$og_description", newsArr.get(newsPosition).getIntro())
.addControlParameter("$og_image_url", newsArr.get(newsPosition).getImage().get(0))
.addControlParameter("$twitter_title", newsArr.get(newsPosition).getTitle())
.addControlParameter("$twitter_description", newsArr.get(newsPosition).getIntro())
.addControlParameter("$twitter_image_url", newsArr.get(newsPosition).getImage().get(0))
;
branchUniversalObject.generateShortUrl(this, linkProperties, new Branch.BranchLinkCreateListener() {
#Override
public void onLinkCreate(String url, BranchError error) {
if (error == null) {
Log.i("MyApp", "got my Branch link to share: " + url);
Intent sharingIntent = new Intent(Intent.ACTION_SEND);
sharingIntent.setType("text/plain");
sharingIntent.putExtra(Intent.EXTRA_SUBJECT, "News Number\n");
sharingIntent.putExtra(Intent.EXTRA_TEXT, url);
startActivity(Intent.createChooser(sharingIntent, "Share via"));
}
}
});
}
}
can anyone help??
thanks in advance.
It seems like you might be trying to make a new Activity for each "page" of a ViewPager. The Android support library already has their own ViewPager that uses fragments.
You basically need three things to use a ViewPager:
The ViewPager itself
An adapter for the ViewPager
The fragment that will be used in the ViewPager (esentially the "template" that will be used for each "page" in the ViewPager)
First, we'll create the fragment. This is just a simple example that uses only one TextView, but it will work for any amount of views and data. You'll notice that static method "newInstance()" that creates and returns an instance of this fragment. Google recommends using this method to instantiate new Fragments for a ViewPager as it makes passing data to the fragment much easier. All fragments have a "setArguments()" method that lets you pass in a Bundle when you instantiate it with whatever data you want. The method "getArguments()" can be used to access that bundle and get the values out of it in onCreate() or onCreateView(). This is how you pass in an object or objects to bind its data to the views. In this example, were just passing in a String and setting that String to a TextView. You'll see how it's used when we create the adapter for the ViewPager.
//You should be using android.support.v4.app.Fragment here
public class ExampleFragment extends Fragment {
private static final String DAY_NAME_KEY = "day_name";
private TextView dayName;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater,
#Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View fragmentView = inflater.inflate(R.layout.fragment_example, container, false);
dayName = (TextView) fragmentView.findViewById(R.id.day_name);
return fragmentView;
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//Set the name of the day to the dayName TextView
String day = getArguments().getString(DAY_NAME_KEY);
dayName.setText(day);
}
public static ExampleFragment newInstance(String day) {
Bundle bundle = new Bundle();
bundle.putString(DAY_NAME_KEY, day);
ExampleFragment fragment = new ExampleFragment();
fragment.setArguments(bundle);
return fragment;
}
}
Now, we need to make an adapter that will create the views when the ViewPager is swiped. You need to extend either FragmentPagerAdapter or FragmentStatePagerAdapter. FragmentPagerAdapter is used for a small number of static fragments and it will use more memory. So you only want to use this if you have 4 or 5 pages and aren't adding any more dynamically.
For your case, it sounds like you want to swipe through a variable number of pages that are dynamically added, so you want to use FragmentStatePagerAdapter. It's optimized for memory efficiency and it will handle the saving and restoring of the fragments' state for you.
The adapter is simple.
First, you need to make your constructor match the super class's constructor. What that means is that your constructor must have a FragmentManager parameter. All you do with that is pass it to the super class. (You'll see below.)
Secondly, you have to implement just 2 methods from the FragmentStatePagerAdapter class:
public Fragment getItem(int position)
and
public int getCount()
getItem will be automatically called by the ViewPager when it is swiped. You just have to tell it what type of fragment to return. We'll be using the newInstance() method from the ExampleFragment class here.
getCount is the number of views that your ViewPager will be using. For example, I will have an ArrayList that will contain 7 strings (one for each day of the week). I want each one of those strings to be displayed on a different page of my ViewPager. So I'm just going to return the size of the ArrayList for this method.
Here's the class:
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
//A reference to the array of data that will be used to populate
//each fragment. In this case it's an array of Strings, but it
//could be any type of object. The Strings within this array are what
//we'll be passing to the newInstance() method of each fragment.
private ArrayList<String> days;
//Constructor must take a FragmentManager to match the superclass.
public ViewPagerAdapter(FragmentManager fm, ArrayList<String> days) {
//All you have to do with the fragment manager is pass it to super
super(fm);
//The array will be passed in when we create the Adapter in our Activity
this.days = days;
}
#Override
public Fragment getItem(int position) {
/*
Automatically called when another page is needed.
The adapter will keep track of the position of the current page
so the first time this is called, it will be 0, then when a
swipe occurs, it will be one. If it is swiped backward, it will go back to 0.
So when the position is 0, a new Fragment will be created and the
String at days.get(0) ("Monday") will be passed to it.
*/
return ExampleFragment.newInstance(days.get(position));
}
#Override
public int getCount() {
return days.size();
}
}
Now we just have to set the adapter for the ViewPager in our Activity and it will automatically handle swipes for us.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayList<String> daysOfWeek = new ArrayList<>();
daysOfWeek.add("Monday");
daysOfWeek.add("Tuesday");
daysOfWeek.add("Wednesday");
daysOfWeek.add("Thursday");
daysOfWeek.add("Friday");
daysOfWeek.add("Saturday");
daysOfWeek.add("Sunday");
ViewPager pager = findViewById(R.id.view_pager);
//Pass the activity's fragmentmanager by calling getSupportFragmentManager().
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager(), daysOfWeek);
pager.setAdapter(adapter);
}
}
This is what we get (I've added some padding and color to make it clear):
As for changing the way the pages "turn" (for example, like a newspaper), you need to look into PageTransformer
I have MainActivity activity which has 3 fragments. Those 3 fragments use same arrayadapter class MessageListAdapter. When i populate listView in my fragments using different ArrayLists using MessageListAdapter it combines all those ArrayLists and displays in each fragment. I want each fragment to display its own list.
MessageListAdapter:
public class MessageListAdapter extends ArrayAdapter<Message>{
Context context;
public MessageListAdapter(Context c, int resourceId, ArrayList<Message> list) {
super(c, resourceId, list);
this.context = c;
}
//...
}
HomeFragment:
public class HomeFragment extends Fragment {
View view;
ListView listView1;
ArrayList<Message> contactMessages;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.home_layout, container, false);
TextView welcomeMessage = (TextView) view.findViewById(R.id.welcomeMessage);
Account acc = new Account();
welcomeMessage.setText("Welcome " + acc.getName() + "!");
contactMessages = new Message().getContactMessages();
listView1 = (ListView) view.findViewById(R.id.homeList);
MessageListAdapter adapter = new MessageListAdapter(this.getActivity(), R.layout.activity_message, contactMessages);
listView1.setAdapter(adapter);
listView1.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// TODO Auto-generated method stub
}
});
return view;
}
}
ProfileFragment:
public class ProfileFragment extends Fragment implements View.OnClickListener, OnItemClickListener {
View view;
Intent intent;
ListView listView2;
ArrayList<Message> personalMessages;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.profile_layout, container, false);
Button button = (Button) view.findViewById(R.id.addMessage);
button.setOnClickListener(this);
Button addFriendButton = (Button) view.findViewById(R.id.addFriend);
addFriendButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
intent = new Intent(getActivity(), AddFriendActivity.class);
startActivity(intent);
}
});
personalMessages = new Message().getPersonalMessages();
// Log.i("Personal messages ArrayList: ", personalMessages.toString());
listView2 = (ListView) view.findViewById(R.id.profileList);
MessageListAdapter adapter = new MessageListAdapter(this.getActivity(), R.layout.activity_message, personalMessages);
listView2.setAdapter(adapter);
listView2.setOnItemClickListener(this);
return view;
}
}
Also have 3rd fragment which will use this same MessageListAdapter, but i have not implemented it yet due to running into this problem.
I made screenshots to make it easier to understand:
Items with orange pictures are supposed to be shown only in ProfileFragment and item with blue picture is supposed to be shown only in HomeFragment
Problem lies in using static ArrayList inside Message class. addPersonalMessage adds Message object into personalMessages list and addContactMessage adds Message object into contactMessages list. After i built all the messages according to their type and put them inside lists separately, for some reason application combines those 2 lists. This is why i end up with similar content in both listviews in fragments. Solved problem by using SQLite database instead of using static variables.
Message:
public class Message {
private String author;
private String messageTitle;
private Bitmap messageImage;
private static ArrayList<Message> personalMessages = new ArrayList<Message>();
private static ArrayList<Message> contactMessages = new ArrayList<Message>();
public Message() {
}
public Message(String a, String t, Bitmap b) {
this.author = a;
this.messageTitle = t;
this.messageImage = b;
}
public void addPersonalMessage() {
personalMessages.add(this);
}
public void addContactMessage() {
contactMessages.add(this);
}
}
i'm pretty new with Fragments and ViewPager. I'm using ActionBarSherlock and ViewPageIndicator from Jack Wharton.
I've started with a standard Android MasterDetailFlow Activity and did try to modify it to use a ViewPager in the detail part.
I'm using the standard DummyContent to provide some static data but i've replaced the DummyItem with my "Survey"-Library i have to use in this app. DummyContent provides a public static ArrayList which i use to fill the list in the list activity. After i choose a survey in this list, the corresponding questions should be shown in the view pager.
Here is the code of my QuestionActivity.java which hosts the question fragments.
public class QuestionActivity extends SherlockFragmentActivity {
private QuestionsFragmentPagerAdapter mAdapter;
private PageIndicator mIndicator;
private ViewPager mPager;
private String surveyName;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_viewpager);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
surveyName = getIntent().getExtras().getString(ItemDetailFragment.ARG_SURVEY_NAME);
mAdapter = new QuestionsFragmentPagerAdapter(getSupportFragmentManager(), DummyContent.mgr.getSurvey(surveyName).getQuestions());
mPager = (ViewPager) findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mIndicator = (PageIndicator) findViewById(R.id.indicator);
mIndicator.setViewPager(mPager);
}
}
QuestionsFragmentPagerAdapter.java
public class QuestionsFragmentPagerAdapter extends FragmentPagerAdapter {
ArrayList<Question> questions;
public QuestionsFragmentPagerAdapter(FragmentManager fm, List<Question> questions) {
super(fm);
this.questions = (ArrayList<Question>) questions;
}
#Override
public Fragment getItem(int position) {
Fragment f = QuestionFragment.newInstance(questions.get(position));
return f;
}
#Override
public int getCount() {
return questions.size();
}
}
QuestionFragment.java
public class QuestionFragment extends SherlockListFragment {
protected enum QuestionType {
FT, SC, MC;
}
public final static String ARG_QUESTION_QUESTION = "question_question";
public final static String ARG_QUESTION_TYPE = "question_type";
public final static String ARG_QUESTION_ANSWERINGOPTIONS = "question_answeringptions";
private TextView lblQuestion;
private EditText txtAnswer;
private ListView listAnswers;
private ArrayAdapter<String> listAdapter;
private Question question;
private int listLayout;
/**
*
* #param question
* #return
*/
public static QuestionFragment newInstance(Question question) {
QuestionFragment fragment = new QuestionFragment();
// Creates a Bundle with all informations available in the question obj.
Bundle args = createBundleFromQuestion(question);
fragment.setArguments(args);
return fragment;
}
/**
*
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Creates the question object from the given arguments.
// I know this isn't a good solution, i will implement the
// Parcelable asap i have solved the current issues.
//
createQuestionFromBundle(getArguments());
// String questionXml = getArguments() != null ? getArguments().getString(ARG_QUESTION_XML) : null;
// this.question = (Question) MyXmlSerializer.deserialize(questionXml, Question.class);
}
/**
* Creates a the Question object form the Bundle.
* #param extras
*/
private void createQuestionFromBundle(Bundle extras) {
// Think we don't need it here. The field question gets instantiated.
}
/**
*
*/
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.answer_question, null);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initWidgets();
setCorrectLayout();
initContent();
}
private void initContent() {
String questionStr = question.getQuestion();
lblQuestion.setText(questionStr);
if(question instanceof FTQuestion) {
} else if (question instanceof ClosedQuestion) {
listAdapter = new ArrayAdapter<String>(getActivity(), listLayout);
List<Answer> answeringOptions = question.getAnswers();
for(Answer answer : answeringOptions) {
listAdapter.add(answer.getAnswer());
}
listAnswers.setAdapter(listAdapter);
}
}
/**
*
*/
private void initWidgets() {
listAnswers = getListView();
lblQuestion = (TextView) getActivity().findViewById(R.id.lblQuestion);
txtAnswer = (EditText) getActivity().findViewById(R.id.txtAnswer);
}
/**
* Sets the FT/SC/MC layout
*/
private void setCorrectLayout() {
if(question instanceof FTQuestion) {
setFtLayout();
} else if (question instanceof SCQuestion) {
setScLayout();
} else if (question instanceof MCQuestion) {
setMcLayout();
}
}
/**
*
*/
private void setFtLayout() {
if(listAnswers.getVisibility()!=ListView.INVISIBLE && listAnswers.getVisibility()!=ListView.GONE) {
listAnswers.setVisibility(ListView.GONE);
}
}
/**
*
*/
private void setScLayout() {
listLayout = R.layout.answer_question_single_choice_list_row;
listAnswers.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
if(txtAnswer.getVisibility() == TextView.VISIBLE) txtAnswer.setVisibility(TextView.GONE);
}
/**
*
*/
private void setMcLayout() {
listLayout = R.layout.answer_question_multiple_choice_list_row;
listAnswers.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
if(txtAnswer.getVisibility() == TextView.VISIBLE) txtAnswer.setVisibility(TextView.GONE);
}
}
Choosing the right survey in the list works fine, but now the questions are displayed totaly wrong.
Actually there should be now 3 pages with 3 different questions. On the first page there should be a label with a question"Eine tolle FT Frage?" and below this label an EditText. On the second page there should be a label with a question "Eine tolle SC Frage?" and below a list with the answering options. On page three the should have the question "Eine tolle MC Frage?" and also a list below it with the same answering options as on page two.
The screenshos show a transition between the pages in the order: 1 -> 2 -> 3 -> 2 -> 1 -> 2.
you can see, that it does not appear in a way i described it above. the content of the pages does also change during the transition. i believe that there could be a problem with the DummyContent because it's static?!
If i create a survey with just one question, everything works fine...
Okay i've found the answer:
i wanted to initialize the used widgets in the onCreateView Callback. But then i always got "java.lang.IllegalStateException: Content view not yet created". A closer look showed, that this was just because of the getListView() method.
Now i switched the initialization of the widgets to the onCreateView() Callback but the getListView() i left in onActivityCreated().
Now everything works fine, and the fragments are displayed correctly!
That's how it looks right now:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.answer_question, null);
lblQuestion = (TextView) v.findViewById(R.id.lblQuestion);
txtAnswer = (EditText) v.findViewById(R.id.txtAnswer);
return v;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
listAnswers = getListView();
setCorrectLayout();
initContent();
}
Add mIndicator.setOnPageChangeListener to your indicator.And send BroadCast inside for current page.
indicator.setOnPageChangeListener(new OnPageChangeListener()
{
#Override
public void onPageSelected(int page) {
switch (page) {
case 0:
sendBroadcast(intent)// update your content.When broadcast come set correct layout.
break;
default:
break;
}
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {}
#Override
public void onPageScrollStateChanged(int arg0) {}
});
Implement broadcast listener to the fragment.
Move your init methods(initWidgets(),initContent())
to a OnCreateView method
It should works
I have created a viewpager layout, a class that extends FragmentActivity and a fragment. What I want is that each fragment get's passed in what position it is within the viewpager. So first viewpager is created getting the argument 0, second getting 1 etc. Then if I scroll one way or another these numbers remain a true count.
The problem is the first time a fragment is created, it seems to be created twice so the position passed is 0 then 1. However I can't scroll back but I know for sure the class is being called twice. Now as I scroll forward the position increases incrementally by one. However if I scroll back it drops immediately to three on just one page back, then continues to drop past the 1 to 0 so now I can finally see my layout for 0.
I have this:
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
PracticeFragment fragment = new PracticeFragment();
getAll.putInt("position", position);
fragment.setArguments(getAll);
return fragment;
}
#Override
public int getCount() {
return numberofQ;
}
}
It first of all runs getItem twice before even going to my fragment class so that the position is 0 then 1. Then when it gets to my fragment class it makes a layout fine, I scroll through a few (3 or 4) new pages and it adds one to the position each time then when I scroll back it says it is zero or two then the numbers continue to be just as sporadic. Finally suddenly when I scroll back to the beginning the position is again 0 so my fragment for position 0 is suddenly displayed.
I don't understand what's happening, so I'm wondering what the mistake is?
public class PracticeFragment extends Fragment {
TextView question, explain;
private ScrollView sv;
private boolean starActionBar;
private final static int version = Consts.SDKversion;
ArrayList<RadioButton> rbArray;
ArrayList<LinearLayout> lArray;
ArrayList<ImageView> ivArray;
int iRow;
SQLite info;
private String correctAnswer;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
info = new SQLite(getActivity());
starActionBar = PreferenceManager.getDefaultSharedPreferences(
getActivity()).getBoolean("star", true);
setHasOptionsMenu(true);
}
#Override
public void onViewStateRestored(Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
for (RadioButton r : rbArray) {
if (r.isChecked()) {
r.performClick();
}
}
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.activity_pm_fragment, menu);
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(
R.layout.activity_main, container, false);
lArray = new ArrayList<LinearLayout>();
rbArray = new ArrayList<RadioButton>();
ivArray = new ArrayList<ImageView>();
lArray.add((LinearLayout) rootView.findViewById(R.id.PM_LinLay0));
lArray.add((LinearLayout) rootView.findViewById(R.id.PM_LinLay1));
lArray.add((LinearLayout) rootView.findViewById(R.id.PM_LinLay2));
lArray.add((LinearLayout) rootView.findViewById(R.id.PM_LinLay3));
lArray.add((LinearLayout) rootView.findViewById(R.id.PM_LinLay4));
for (LinearLayout l : lArray) {
l.setOnTouchListener(PracticeFragment.this);
l.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (((ViewGroup) v).getChildAt(0).isEnabled()) {
((ViewGroup) v).getChildAt(0).performClick();
}
}
});
}
rbArray.add((RadioButton) rootView.findViewById(R.id.radio0));
rbArray.add((RadioButton) rootView.findViewById(R.id.radio1));
rbArray.add((RadioButton) rootView.findViewById(R.id.radio2));
rbArray.add((RadioButton) rootView.findViewById(R.id.radio3));
rbArray.add((RadioButton) rootView.findViewById(R.id.radio4));
ivArray.add((ImageView) rootView.findViewById(R.id.ivradio0));
ivArray.add((ImageView) rootView.findViewById(R.id.ivradio1));
ivArray.add((ImageView) rootView.findViewById(R.id.ivradio2));
ivArray.add((ImageView) rootView.findViewById(R.id.ivradio3));
ivArray.add((ImageView) rootView.findViewById(R.id.ivradio4));
rootView.findViewById(R.id.bNext).setVisibility(View.GONE);
rootView.findViewById(R.id.bPrevious).setVisibility(View.GONE);
sv = (ScrollView) rootView.findViewById(R.id.svMain);
info.open();
iRow = Integer.valueOf(info.getEverything(getArguments(), getArguments().getInt("position"), "next"));
Cursor c = info.getCursor(iRow);
((TextView) rootView.findViewById(R.id.tvQuestion))
.setText((getArguments().getInt("position") + 1) + ") " + c.getString(2));
explain = (TextView) rootView.findViewById(R.id.tvExplain);
explain.setText(c.getString(9));
explain.setVisibility(View.GONE);
correctAnswer = c.getString(8);
String[] aArray = { c.getString(3), c.getString(4), c.getString(5),
c.getString(6), c.getString(7) };
c.close();
info.close();
int o = 0;
int pos = 0;
for (String s : aArray) {
LinearLayout l = lArray.get(pos);
if (s.contentEquals("BLANK")) {
l.setVisibility(View.GONE);
} else {
l.setVisibility(View.VISIBLE);
rbArray.get(pos).setText(s);
rbArray.get(pos).setOnClickListener(null);
if (o % 2 == 0) {
l.setBackgroundColor(Consts.colorAlt);
}
o++;
}
pos++;
}
return rootView;
}
}
However if I comment out everything but the viewgroup and return rootview - still the same problem.
initialize the getAll every time as a new object in getItem()
make your fragment class static
and create one method in PracticeFragment
static PracticeFragment newInstance(int num) {
PracticeFragment f = new PracticeFragment();
// Supply num input as an argument.
Bundle args = new Bundle();
args.putInt("num", num);
f.setArguments(args);
return f;
}
and change in adapter
#Override
public Fragment getItem(int position) {
return PracticeFragment.newInstance(position);
}
Subclassing it fixed the problem!