App kept stop running in Emulator in Android Studio - java

I've been following a tutorial on YouTube about tab layouts and when I was finish following it and run the application, the app kept stop running. So, I open my logcat to see what was wrong and it says: "Caused by java.lang.ClassCastException: androidx.viewpager.widget.ViewPager cannot be cast to com.google.android.material.tabs.TabLayout". I was unable to find what "cannot be cast to" means and can't find any documents explaining it.
private Toolbar toolbar;
private ViewPager viewPager;
private TabLayout tabLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = findViewById(R.id.toolbarClipBridge);
setContentView(R.layout.activity_main);
viewPager = findViewById(R.id.viewpagerClipBridge);
tabLayout = findViewById(R.id.viewpagerClipBridge);
fragmentDevices fragmentDevices = new fragmentDevices();
fragmentSyncActivity fragmentSyncActivity = new fragmentSyncActivity();
fragmentSettings fragmentSettings = new fragmentSettings();
ViewPageAdapter viewPageAdapter = new ViewPageAdapter(getSupportFragmentManager(), 0);
viewPageAdapter.addFragment(fragmentDevices, "Devices");
viewPageAdapter.addFragment(fragmentSyncActivity, "Sync Activity");
viewPageAdapter.addFragment(fragmentSettings, "Settings");
viewPager.setAdapter(viewPageAdapter);
tabLayout.getTabAt(0).setIcon(R.drawable.ic_baseline_devices_24);
tabLayout.getTabAt(1).setIcon(R.drawable.ic_baseline_sync_24);
tabLayout.getTabAt(2).setIcon(R.drawable.ic_baseline_settings_24);
}
private class ViewPageAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments = new ArrayList<>();
private List<String> fragmentTitle = new ArrayList<>();
public ViewPageAdapter(#NonNull FragmentManager fm, int behavior) {
super(fm, behavior);
}
public void addFragment(Fragment fragment, String title){
fragments.add(fragment);
fragmentTitle.add(title);
}
#NonNull
#Override
public Fragment getItem(int position) {
return fragments.get(position);
}
#Override
public int getCount() {
return fragments.size();
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
return fragmentTitle.get(position);
}
}
Logcat:
Caused by: java.lang.ClassCastException: androidx.viewpager.widget.ViewPager cannot be cast to com.google.android.material.tabs.TabLayout
Image of my logcat

In the line:
viewPager = findViewById(R.id.viewpagerClipBridge);
tabLayout = findViewById(R.id.viewpagerClipBridge);
The code tries to look into your activity_main.xml file and find an XML object with the id R.id.viewpagerClipBridge. The XML object is probably of the type ViewPager.
In the following line, the code looks again into the XML file. Again, with the id R.id.viewpagerClipBridge, the code tries to set the return value to a variable tab layout of type TabLayout.
And since the type of ViewPager != type of TabLayout it cannot be cast to that type.
I think you used the wrong id in the second findViewById line. Investigate your activity_main.xml and look for the id of your TabLayout.

Related

ImageView Change after SwipeTabs Android

After I take my Photo and Signature, it displays on the ImageView. However, after I swap the tabs the ImageView returns to its default Image. See my screenshot.
There's no problem when it comes to editText the inputted value is still there. However, the ImageView is my problem. What's causing this problem? And how can I fix this?
Code:
public class RegisterUser extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v =inflater.inflate(R.layout.activity_register_user,container,false);
// Setting ViewPager for each Tabs
ViewPager viewPager = (ViewPager) v.findViewById(R.id.viewpager);
setupViewPager(viewPager);
// Set Tabs inside Toolbar
TabLayout tabs = (TabLayout) v.findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);
// Create Navigation drawer and inlfate layout
return v;
}
// Add Fragments to Tabs
private void setupViewPager(ViewPager viewPager) {
Adapter adapter = new Adapter(getChildFragmentManager());
adapter.addFragment(new ListContentFragment(), "Info 1");
adapter.addFragment(new TileContentFragment(), "info 2");
adapter.addFragment(new CaptureSignatureActivity(), "info 3");
adapter.addFragment(new CardContentFragment(), "info 4");
viewPager.setAdapter(adapter);
}
static class Adapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public Adapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
}
When you swipe through tabs (Fragments, actually) in a TabLayout, the default behaviour is that the system kills the previous Fragment to free memory and the subsequent Fragment to the current one will be loaded in the memory.
Now, with your code something similar is happening. When you load a photo and a signature in the 3rd tab (CaptureSignatureFragment) and then swipe through the tabs, the CaptureSignatureFragment is killed and thus both the ImageViews have the default images set to them.
If you wish to keep the data in the 3rd tab intact even after changing tabs, you should better set your ViewPager's offscreen limit to 4 (no. of tabs you have) by:
viewPager.setOffscreenPageLimit(4)
Now, changing the tabs won't kill any of the fragments. However, doing this can create performance issues.

Why is accessing TextView of a Fragment inside Activity throwing an error

MainActivity class:
/* all necessary imports */
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
/* Other variable initialized here... */
FragOne fo;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
fo.setTextViewText("This is added from Activity");
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
viewPager = (ViewPager) findViewById(R.id.viewpager);
setupViewPager(viewPager);
tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
}
private void setupViewPager(ViewPager viewPager) {
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFragment(new FragOne(), "My Tracker");
adapter.addFragment(new FragTwo(), "Team Tracker");
viewPager.setAdapter(adapter);
}
class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
#Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
#SuppressWarnings("StatementWithEmptyBody")
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
if (id == R.id.nav_manage) {
} else if (id == R.id.nav_share) {
} else if (id == R.id.nav_send) {
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
}
Fragment class:
/* all necessary imports */
public class FragOne extends Fragment {
TextView tvCName;
public FragOne() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_frag_one, container, false);
return view;
//return inflater.inflate(R.layout.fragment_frag_one, container, false);
}
#Override
public void onViewCreated(View view , Bundle savedInstanceState) {
tvCName = (TextView) view.findViewById(R.id.tvctq);
}
public void setTextViewText(String value){
tvCName.setText(value);
}
}
Fragment XML Layout:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.mytip.FragOne">
<TextView
android:text="TextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/tvctq" />
</FrameLayout>
I am trying to access the TextView inside the Fragment from MainActivity like this:
FragOne fo;
fo.setTextViewText("This is added from Activity");
I keep getting a NullPointerExceptionError. I looked at all the articles to see how to access, however none of them helped me.
Can someone please let me know what am I doing wrong and how to fix it?
I also plan on adding other Views inside my Fragment that I would need to access in the future.
Because fo hasn't been initialized in the following code snippet:
FragOne fo;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
fo.setTextViewText("This is added from Activity");
...
}
fo.setTextViewText() reasonably throws NPE.
You have to pay attention to the Activity lifecycle - you seem to be setting everything up correctly, but making a few mistakes accessing the correct instance of the fragment at the time it's actually ready. Things you should do
Get proper instance of the fragment from your ViewPager, like #ginomempin suggested;
Only try to set your text no earlier then your activities onStart method has been called - I usually do it onResume method (you can override it if you haven't already). Doing it in onResume method in the activity makes sure your Fragment has already gone through it's lifecycle up till onResume as well, and data will refresh if it has been brought to the background previously.
Here's a lifecycle diagram for your reference:
You need to use your Fragment factory method when creating your Fragment in your activity. Please see below:
**
Back Stack
**
The transaction in which fragments are modified can be placed on an internal back-stack of the owning activity. When the user presses back in the activity, any transactions on the back stack are popped off before the activity itself is finished.
For example, consider this simple fragment that is instantiated with an integer argument and displays that in a TextView in its UI:
public static class CountingFragment extends Fragment {
int mNum;
/**
* Create a new instance of CountingFragment, providing "num"
* as an argument.
*/
static CountingFragment newInstance(int num) {
CountingFragment f = new CountingFragment();
// Supply num input as an argument.
Bundle args = new Bundle();
args.putInt("num", num);
f.setArguments(args);
return f;
}
/**
* When creating, retrieve this instance's number from its arguments.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mNum = getArguments() != null ? getArguments().getInt("num") : 1;
}
/**
* The Fragment's UI is just a simple text view showing its
* instance number.
*/
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.hello_world, container, false);
View tv = v.findViewById(R.id.text);
((TextView)tv).setText("Fragment #" + mNum);
tv.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb));
return v;
}
}
A function that creates a new instance of the fragment, replacing whatever current fragment instance is being shown and pushing that change on to the back stack could be written as:
void addFragmentToStack() {
mStackLevel++;
// Instantiate a new fragment.
Fragment newFragment = CountingFragment.newInstance(mStackLevel);
// Add the fragment to the activity, pushing this transaction
// on to the back stack.
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.simple_fragment, newFragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();
}
After each call to this function, a new entry is on the stack, and pressing back will pop it to return the user to whatever previous state the activity UI was in.
Source: https://developer.android.com/reference/android/app/Fragment.html
You need to get the same instance of FragOne from the viewpager.
First, you can only access the FragOne instance after the ViewPager is setup.
Then, try this:
fo = adapter.getItem(0)
Note:
Since you already have fragments, it would be better to let the fragment itself handle the UI-related actions (such as setting the textview) rather than from the Activity.

ClassCastException: MainActivity cannot be cast to Listener

I'm trying to implement a WifiScanner Listener to my ScanFragment but I'm receveing this error: java.lang.ClassCastException: emilsoft.wifitest3.MainActivity cannot be cast to emilsoft.wifitest3.WifiScanner$Listener
I already did this with normal Activities and now I'm trying to convert it to Fragments, which I'm currently learning about them.
I did a lot of research but I couldn't find a working solution. I have commented the code where there is the error
So my Main Activity:
private SectionsPagerAdapter mSectionsPagerAdapter;
private ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(mViewPager);
}
My SectionsPagerAdapter class :
public class SectionsPagerAdapter extends FragmentPagerAdapter{
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0: return ScanFragment.newInstance();
}
return null;
}
My ScanFragment :
public class ScanFragment extends Fragment implements WifiScanner.Listener {
private ScanCollector sc;
private WifiManager wifi;
public ScanFragment() {}
public static ScanFragment newInstance() {
return new ScanFragment();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View result = inflater.inflate(R.layout.fragment_scan_results, container, false);
wifi = (WifiManager) getActivity().getSystemService(Context.WIFI_SERVICE);
sc = new ScanCollector(this.getContext()); //THE ERROR STARTS HERE
return result;
}
My ScanCollector class(which handle the listeners added to the WifiScanner class):
public class ScanCollector {
// The context wrapper that we'll use for accessing system services, receiving
// broadcasts from the WifiManager
private final Context context;
private WifiScanner.Listener listener;
public ScanCollector(Context context) {
if (context == null)
throw new NullPointerException();
this.context = context;
this.listener = (WifiScanner.Listener)context; //THE ERROR IS HERE
}
The problem is that I can't pass the correct Context to my ScanCollector class which will then cast it to a WifiScanner.Listener. Probably is a very stupid solution but I can't find it.
Thanks in advance!
One thing is the context and another is the WifiScanner.Listener. Your ScanCollector needs both so pass both of them:
public ScanCollector(Context context, WifiScanner.Listener listener) {
if (context == null)
throw new NullPointerException();
this.context = context;
this.listener = listener
}
And when you create it:
sc = new ScanCollector(getActivity(), this);
Change
sc = new ScanCollector(this.getContext()); // The fragment's context
to
sc = new ScanCollector(getActivity()); // The activity's context
this.getContext() refers to the current context of the fragment, which is different than the context of the host activity that truly implements the Listener interface.
(Make sure that MainActivity implements WifiScanner.Listener)
Add implements to your class implementing listener ( you must find your listener name from example ) ... Then click Alt+Enter and select implement methods from right click options

RecyclerView leaks in FragmentPagerAdapter

I've figured out that in defined concatenation of circumstances RecyclerView leads to memory leak. To archive such effect, I have created FragmentPagerAdapter that contains fragment with RecyclerView as child.
In case of going application to background or finishing, leak canary fires memory leak alert. Here's my activity class
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ViewPager mViewPager = (ViewPager) findViewById(R.id.view_pager);
List<Fragment> fragments = new LinkedList<>();
for (int i = 0; i < 10; i++) {
fragments.add(CustomFragment.newInstance(i));
}
mViewPager.setAdapter(new CustomPagerAdapter(getFragmentManager(), fragments));
mViewPager.setCurrentItem(0, false);
}
public class CustomPagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments;
public CustomPagerAdapter(FragmentManager fragmentManager, List<Fragment> fragments) {
super(fragmentManager);
this.fragments = fragments;
}
#Override
public Fragment getItem(int position) {
return fragments.get(position);
}
#Override
public int getCount() {
return fragments.size();
}
}
}
To get the leak, RecyclerView even doesn't need to be initialized. If I comment it in xml file, the leak isn't fired
public class CustomFragment extends Fragment {
public static final String POSITION = "position";
public static CustomFragment newInstance(int position) {
Bundle b = new Bundle();
b.putInt(POSITION, position);
CustomFragment fragment = new CustomFragment();
fragment.setArguments(b);
return fragment;
}
private int position;
private RecyclerView recyclerView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
position = getArguments().getInt(POSITION);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_custom, container, false);
TextView textView = (TextView) v.findViewById(R.id.text);
textView.setText("Position: "+position);
recyclerView = (RecyclerView) v.findViewById(R.id.pulse_recyclerview);
return v;
}
#Override
public void onDestroy() {
super.onDestroy();
RefWatcher refWatcher = CustomApplication.getRefWatcher(getActivity());
refWatcher.watch(this);
}
}
Here's leak trace. RecyclerView version 23.1.1
In com.example.gabin.sampleapplication:1.0:1.
* LEAK CAN BE IGNORED.
* com.example.gabin.sampleapplication.MainActivity has leaked:
* GC ROOT static android.view.inputmethod.InputMethodManager.sInstance
* references android.view.inputmethod.InputMethodManager.mCurRootView
* references com.android.internal.policy.impl.PhoneWindow$DecorView.mContext
* leaks com.example.gabin.sampleapplication.MainActivity instance
* Retaining: 3,9 КБ.
* Reference Key: 67c5e9f4-464e-40c3-b21d-d802fe64a84b
* Device: LGE google Nexus 4 occam
* Android Version: 5.1.1 API: 22 LeakCanary: 1.4-beta1 02804f3
* Durations: watch=5035ms, gc=190ms, heap dump=5948ms, analysis=46384ms
May it be an Android bug?
Please, give any idea how to fix the leak, or help me figure out the reason of its firing by leakcanary.
Your stack trace shows at least two things:
It is not RecyclerView who leaks activity. It is the ImputMethodManager.
LEAK CAN BE IGNORED. According to Leak Canary docs it is a known sdk problem. But I don't think its really an activity leak. If you check your memory dump you will not see several activity instances. Code looks pretty safe at now.

Calling a method in a fragment in a FragmentStatePagerAdapter from the activity that created it

I have an activity that has a navigation drawer and a Fragment state adapter. When ever I click something in the navigation drawer I am passing that info to the mainactivity via a interface.And then I am passing that data to the main fragment which is in Fragmentstateadapter. The problem is when ever the screen rotates I get a null pointer exception for the main fragment.`
public class MainActivity extends ActionBarActivity implements RightDrawerRecyclerAdapter.Filters,
LeftDrawerFragment.MenuSection,CommonAsyncTask.ServerData {
private static final String TAG = MainActivity.class.getSimpleName();
public MainFragment mMainFragment;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewPager pager = (ViewPager) findViewById(R.id.pager);
ScreenSlidePagerAdapter pagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
pager.setAdapter(pagerAdapter);
}
#Override
public void getMenuSelection(int selection) {
Log.d(TAG,"getMenuSelection->"+selection);
mMainFragment.getMenuSelection(selection);
}
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
Log.d(TAG, "in screenslide");
}
#Override
public Fragment getItem(int position) {
if (position == 0) {
Log.d(TAG, "in returning MainFragment");
mMainFragment=new MainFragment();
return mMainFragment;
} else {
return new MapViewFragment();
}
}
#Override
public int getCount() {
return 2;
}
#Override
public CharSequence getPageTitle(int position) {
if (position == 0) {
return "List";
} else {
return "Map";
}
}
}}
So getMenuSelection is the method from the interface in navigation drawer. Then I have a method with the same name in my main fragment in MainFragment.So when ever I select something in the navigation drawer I can change contents in the main fragment but when ever I turn my screen a new instance of MainFragment is getting created and getItem(int position) is not getting trigged so my MainFragment object is null and now I cant pass data from my navigation drawer to main fragment.I tried making MainFragment object as static and that created problems in my MainFragment class.
So how can I get back the same MainFragment object even when the screen is turned. Or is there any better way to pass data between navigation drawer fragment and a fragment in FragmentState adapter
Hi guys I solved the problem by sending back the current instance of the MainFragment to MainActivity from onAttach via interface

Categories