View pager returns black screen - android

I have a ViewPager that displays GPUImageViews. When creating instances for each page, everything seems great. The problem comes when I tried swiping backwards, to load the previous page. Here's my onCreateView code.
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//I store my previous pages in an imageArray variable to prevent application from rendering image over and over again.
temp = imageArray[this.index]; //temp is a GPUImageView; assume initialized
if (temp.getParent() != null) {
((ViewGroup) temp.getParent()).removeView(temp);
}
temp.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
return temp;
}
When I added removeView() the view pager always return black screen. I tried taking off the removeView() but then it always gives me the error "child view has existing parent...remove parent view first."
EDIT:
to be more specific, i added my onCreate(), init() and renderImage() functions. by the way, this is a Fragment and for my PagerAdapter i'm using FragmentPagerAdapter
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().findViewById(R.id.saveBtn).setOnClickListener(this);
this.init();
this.renderImage();
}
private void init() {
temp = new GPUImageView(getActivity());
this.img = new GPUImageView(getActivity()); //the original image
this.img.setImage(imgUri);
}
private void renderImage() {
temp = this.img;
temp.setFilter(Filter.setFilter(getActivity(), this.filterPosition));
temp.requestRender();
imageArray[this.filterPosition] = temp;
}
I tried debugging to see how my app works...It seems like when creating new instances, onCreate() will always be called followed by onCreateView(). Based from the lifecycle of this guide, I thought only onCreateView() will be called repeatedly.

Related

Reusing layout created on Fragment.onCreateView() to avoid inflation when showing multiple times

I am using a DialogFragment to display a 'modal' bottom sheet menu (more info here: https://material.io/develop/android/components/bottom-sheet-dialog-fragment/). Since it contains a kind of context menu for the items contained in a RecyclerView, it may be shown multiple times during runtime.
However, always DialogFragment.show() is called, Fragment.onCreateView() is also called, which leads to layout inflation, which can(?) be considered as a 'heavy' task to be computed in the UI thread, which I want to avoid for performance reasons. So to avoid layout inflation every time the DialogFragment is shown, I created a ViewGroup member object pointing to the View being returned Fragment.onCreateView() in order to be reused, like this:
public class BottomMenu extends BottomSheetDialogFragment {
private ViewGroup mLayout;
private TextView mLabel;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
if (mLayout == null) {
mLayout = (ViewGroup) LayoutInflater.from(getContext()).inflate(R.layout.bottom_sheet, container, false);
mLabel = mLayout.findViewById(R.id.bottom_sheet_label);
}
return mLayout;
}
#Override
public void onDismiss(#NonNull DialogInterface dialog) {
super.onDismiss(dialog);
// The view cannot be reused if it's already attached to the previous parent view
((ViewGroup) mLayout.getParent()).removeView(mLayout);
}
public void setLabel(String label) {
mLabel.setText(label)
}
}
But once used for the first time, such view must be detached from the Fragment container view to be reused (see onDismissed() overriden method on posted snippet), which seems like a nasty workaround
So I post this question to check if anyone knows a better approach to reuse the layout for the same Fragment
More details here:
public class ActivityMain extends AppCompatActivity {
private BottomMenu mBottomMenu;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
[...]
mBottomMenu = new BottomMenu();
}
#Override
public boolean onLongClick(View v) {
mBottomSheet.setLabel(label);
// The following calls onCreateView() in Fragment, so try to return
// there the previously inflated layout, if any
mBottomSheet.show(getSupportFragmentManager(), "TAG?");
return true;
}
}
It is already a nice practice as long as you don't surrender to any possible bugs.. However there are one or two things I want to let you know about resuing dialogFragment.
public class BottomMenu extends BottomSheetDialogFragment {
private ViewGroup mLayout;
private TextView mLabel;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
if (mLayout == null) {
mLayout = (ViewGroup) LayoutInflater.from(getContext()).inflate(R.layout.bottom_sheet, container, false);
mLabel = mLayout.findViewById(R.id.bottom_sheet_label);
} else if(mLayout.getParent()!=null) { // it's not a lot of code. just a few lines……
((ViewGroup)mLayout.getParent()).removeView(mLayout);
}
return mLayout;
}
}
One thing is about nested fragments. When the dialogFragment hold a viewpager and the viewpager have serveral sub-fragments, you must reset the viewpager's adapter on the reusing-call of onCreateView. The reason is that after closing the dialogFragment, the old fragmentManager returned by getChildFragmentManager() is no longer valid, and it should be updated.
... onCreateView(...)
if (mLayout == null) {
...
} else {
...
viewpager.setAdapter(new MyFragmentAdapter(getChildFragmentManager(), fragments));
}
If this step is omitted, you may observe strange behaviours when reusing the dialogFragment, such as recyclerviews in the sub-fragments stop updating in response to NotifyDatasetChanged, but if you scroll it, it will update.
Another thing is that I tend to use WeakRefernce to hold the dialogFragment to be reused. I even have an array of them.
In java applications, if you don't use similar mechanism, you can see rapid surge in memory usage when the user open and close the same dialog again and again. So at least it's not a bad practice to reuse dialogs when it's necessary.

Give a certain EditText focus

I'm using 2 EditText next to each other, the left one gains focus on the fragment startup, I want to give the right one focus I've tried to call requestFocus() on the right EditText but it's not working
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.sandwich_fragment,container, false);
sandwichNameEditText = view.findViewById(R.id.sandwich_name_edit_text);
sandwichPriceEditText = view.findViewById(R.id.sandwich_price_edit_text);
insertSandwichImageView = view.findViewById(R.id.insert_sandwich_btn);
sandwichListView = view.findViewById(R.id.sandwich_list);
dbHandler = new DBHandler(getContext(),null);
sandwichArrayList = dbHandler.getSandwiches();
final SandwichListAdapter adpater = new SandwichListAdapter(getContext(),
R.layout.sandwich_item, sandwichArrayList);
sandwichListView.setAdapter(adpater);
insertSandwichImageView.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
if(sandwichNameEditText.getText().toString().equals("") || sandwichPriceEditText.getText().toString().equals("")){
Toast.makeText(getContext(),"No empty strings.", Toast.LENGTH_SHORT).show();
return;
}
Sandwich sandwich = new Sandwich(sandwichNameEditText.getText().toString(),
Double.parseDouble(sandwichPriceEditText.getText().toString()));
dbHandler.addSandwich(sandwich);
adpater.add(sandwich);
sandwichNameEditText.setText("");
sandwichPriceEditText.setText("");
sandwichNameEditText.requestFocus(); // working here
}
});
sandwichNameEditText.requestFocus(); // not working here
return view;
Try to call requestFocus in the onViewCreated method.
The request for focus is something you should do once your View is created.
You can find some insights about the difference between onCreateView and onViewCreated methods for a Fragment here.
That said, you should move your view elements initialisations in the onViewCreated as well, since they're something you want to do after the view is created and not while it's being created. Just leave the inflate logic there, and do the other logic once the View is there.

Xamarin FindViewById NullReference Exception

I have a problem, whenever i try to add an eventhandler to a button i get a null reference exception, i am trying to create a popup window with a DialogFragment, where inside it im calling the view PopUpWindow wich will show up on screen, but when i try to access the buttons by id and to assign them eventhandlers for example:
Button btnCopyText = dp.view.FindViewById<Button>(Resource.Id.btnCopyText);
btnCopyText.Click += BtnCopyText_Click;
then i get a null reference exception, can anyone help me, below is the necessary code.
class dialog_Popup:DialogFragment
{
public View view;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
base.OnCreateView(inflater, container, savedInstanceState);
view = inflater.Inflate(Resource.Layout.PopupWindow, container, false);
return view;
}
public override void OnActivityCreated(Bundle savedInstanceState)
{
Dialog.Window.RequestFeature(WindowFeatures.NoTitle);
base.OnActivityCreated(savedInstanceState);
}
public class MainActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
//some code
}
public string itemclicked;
dialog_Popup dp;
private void Lv_ItemLongClick(object sender, AdapterView.ItemLongClickEventArgs e)
{
//View popUpView = LayoutInflater.Inflate(Resource.Layout.PopupWindow,
//null); // inflating popup layout
Button height = FindViewById<Button>(Resource.Id.btnCopyText);
//Then: change the width of the button
FragmentTransaction transaction = FragmentManager.BeginTransaction();
dp = new dialog_Popup();
dp.Show(transaction,"Popup");
itemclicked = lv.GetItemAtPosition(e.Position).ToString();
Button btnCopyText = dp.view.FindViewById<Button>(Resource.Id.btnCopyText);
btnCopyText.Click += BtnCopyText_Click;
Button btnSaveCurrentAya = dp.view.FindViewById<Button>(Resource.Id.btnSaveCurrentAya);
btnSaveCurrentAya.Click += BtnSaveCurrentAya_Click;
Button btnsavingsAya = dp.view.FindViewById<Button>(Resource.Id.savingsAya);
btnsavingsAya.Click += BtnsavingsAya_Click;*
Button btnShareFB = dp.view.FindViewById<Button>(Resource.Id.fbShare);
btnShareFB.Click += BtnShareFB_Click;
}
}
There are several reasons why a NullReferenceException can occur with FindViewById:
The layout does not contain the id -> check that the correct layout and id is inflated/ referenced
The type like Button is incorrect
In your case, check that dp and dp.view is not null.
One thing to mention here is, that it is not the best implementation to reference the control of a fragment in you main view. A fragment is something that should be able to life on her own. So I see two ways of implementing your desired behavior:
1) The fragment gets an event and you listen to that. This means your main view will contain the logic to save something.
2) The logic moves into the fragment.

TextView forgets its value after tab switching

I've setup a tabhHost in my program with 3 tabs each with a fragment in its content that contains a textView. I've set a button in app that is supposed to update the content of every tab. However the problem i am now facing is that if i change a tab its content gets forgotten and new tab has no value aswell unless i click the button again. Each tab content has a different class but in each of them the class just returns the view with textview in it. I assumed that the values reset on each view inflate however i cannot find a reasonable way to make it work as i intend to it.
Here is the part of the code with the method ran by my button:
public void getNum(View view) {
proteinNeeded = weightNum.getValue() * FirstActivity.weightMultiplier;
carbohydrateNeeded = weightNum.getValue() * 5;
switch (weightMultiplier) {
case 2: {
resultTxt = String.format("Zapotrzebowanie:%nBiałko - %dg.%nWęglowodany - %dg.%n",
FirstActivity.proteinNeeded, FirstActivity.carbohydrateNeeded);
FragmentTab.tv.setText(resultTxt);
FragmentTab2.dietTxt = String.format("text1");
FragmentTab2.tv.setText(FragmentTab2.dietTxt);
FragmentTab3.foodTxt = String.format("text2");
FragmentTab3.tv.setText(FragmentTab3.foodTxt);....
And here is the code from FragmentTab.java, each tab has more or less same code so i'll post only this one for now:
public class FragmentTab extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public static View v;
public static TextView tv;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
v = inflater.inflate(R.layout.fragment_layout, container, false);
tv = (TextView) v.findViewById(R.id.textResult);
if (tv != null) {
tv.setText("nothing has been input yet");
}
return v;
}
}
TL;DR - How do I save my values in textView in such a way that if my text gets updated it won't revert to default values after the view gets refreshed?
An option to solve your problem is to use SQLite Database to save your data, and fetch it from database whenever you return to that tab.
You can refer to this link. Saving Data in SQL Databases.
For other android storage options, refer to this series of videos. Android Data Storage Options.

Android: ListFragment display blank space

I have a ListView (inside a ListFragment) loading data with the help of a LoaderManager, to which I attach a headerView and a footerView. My problem is that after doing that, it is displaying a lot of blank space (like almost an entire screen) after the footer is displayed.
Some code from my ListFragment:
public class NewsDetailsFragment extends ListFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getArguments().containsKey(Const.ARG_ITEM_ID)) {
mId = getArguments().getLong(Const.ARG_ITEM_ID);
getLoaderManager().initLoader(NEWS_DETAILS_LOADER_ID, null, this);
getLoaderManager().initLoader(COMMENTS_LOADER_ID, null, this);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mCommentsAdapter = new CommentsListAdapter(getActivity(), null, 0);
View rootView = inflater.inflate(R.layout.frag_news_details_comments,
container, false);
return rootView;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mHeaderView = LayoutInflater.from(getActivity()).inflate(
R.layout.header, getListView(), false);
mFooterView = LayoutInflater.from(getActivity()).inflate(
R.layout.footer, getListView(), false);
setUI();
setAction();
if (mHeaderView != null) {
getListView().addHeaderView(mHeaderView);
}
if (mFooterView != null) {
getListView().addFooterView(mFooterView);
}
setListAdapter(mCommentsAdapter);
}
Obviously, this is not the entire code from my ListFragment class, but the other stuff is mostly related to how the data is being manipulated (Loader Callbacks and others). If there is a need to add full code in order to be able to get the correct answer, I will add it on demand.
As for a general idea of how the screen should look like, the header displays some data about the article (which contains a bunch of TextViews, ImageViews and a Webview), the ListView displays comments on this article (if there are any), and the footer displays a form with writing a comment.
LE: This is how it looks like:
LE2: This screenshot shows how headerView and footerView are limited.
headerView ends at "0 comments on this post:" and after that comes the footerView. In between these two, should be displayed the list of comments (if there are any)
LE3: To make myself even more clear: I will display the wrong (red), and right (green) way it should be displayed.
Turns out I had this background on footerView (the same of the ListView), just to test if everything looked fine, with textColor and stuff, and I forgot to remove it afterwards. Then it took its height, which was larger than the form in the footer, thus creating the empty space. Sry to have bothered your eyes with such stupidity.

Categories

Resources