Showing a Fragment as BottomSheet in Android - java

In android latest versions, a new feature of Bottom sheets has been introduced which allows showing popups as bottom sheets. For example,
BottomSheetDialog bottomSheetDialog = new BottomSheetDialog(this);
View sheetView = getLayoutInflater().inflate(R.layout.any, null);
bottomSheetDialog.setContentView(sheetView);
bottomSheetDialog.show();
And you can show the popup as bottom sheet view.
My problem is that I have to do a lot of code for my apps functionality say 100-200 lines of code but keeping this code inside same activity or fragment makes my code ugly and unmanageable. Is there a way that I can extract out this code to some Fragment or other activity such that all the code related to bottom sheet dialog remains inside it's own class.
I am not looking up for any java hacks. Rather android way of doing this is expected.
How to open and close the bottomsheet view?

consider to use BottomSheetDialogFragment instead

As Zafer Celaloglu said can do so
kotlin
class MyDialogFragment : BottomSheetDialogFragment() {
companion object {
const val TAG = "FilterDialogFragment"
fun newInstance() = FilterDialogFragment()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_dialog_my_layout, container, false)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
}
java
public class MyDialogFragment extends BottomSheetDialogFragment {
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_dialog_my_layout, container, false)
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
and call in activity like that
kotlin
MyDialogFragment.newInstance().show(supportFragmentManager,MyDialogFragment.TAG)
java
MyDialogFragment myDialogFragment = new MyDialogFragment();
myDialogFragment.show(getSupportFragmentManager(), "MyDialogFragment");

Related

How to send data from Activity to Fragment using Interface and without the transaction?

I want to send the data from MainActivity to Fragment but i don't want to pass the data in the arguments when i am making the transaction to the fragment like below:
val bundle = Bundle()
bundle.putString("name", name)
val fragment = BlankFragment(this)
fragment.arguments = bundle
supportFragmentManager.beginTransaction().replace(R.id.fragmentView, fragment).commit()
I want to pass the data from the activity to the fragment using the interface. I am able to send the data from the fragment to the activity using the interface but not vice versa.
You have an instance of a fragment in fragment, so provided it has a property name you can just say fragment.name = "name".
Note that when the app is restarted by the system after it is killed, the system will try to restore the fragment by calling it with empty constructor. So you can't have a constructor that have arguments, and also you won't get a name if you set it using fragment.name = "name". Using a bundle ensures that that the fragment gets its arguments after restart.
I hope this can resolve your issue
Adding fragment to Activity
val interfaceCall:FetchDataInterface = DataFragment.newInstance("data")
interfaceCall.getData("data from interface")
supportFragmentManager.beginTransaction()
.replace(R.id.frame, DataFragment.newInstance("data"))
.commit()
Fragment Class
class DataFragment : Fragment(),MainActivity.FetchDataInterface {
// TODO: Rename and change types of parameters
private var param1: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(DATA)
Log.e("checkParam", " : $param1")
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_data, container, false)
}
companion object {
// TODO: Rename and change types and number of parameters
#JvmStatic
fun newInstance(value: String) =
DataFragment().apply {
arguments = Bundle().apply {
putString(DATA, value)
}
}
}
override fun getData(value: String) {
Log.e("checkParam", " interface: $value")
}
}
Interface
interface FetchDataInterface{
fun getData(value:String)
}
Tryout this onces
In activity.....
TextEditorDialogFragment textEditorDialogFragment =
TextEditorDialogFragment.show(this, text, colorCode);
In Fragment....
public class TextEditorDialogFragment extends DialogFragment {
public static final String TAG = TextEditorDialogFragment.class.getSimpleName();
String inputText;
int colorCode;
public static TextEditorDialogFragment show(#NonNull AppCompatActivity appCompatActivity, #NonNull String inputText_, #ColorInt int colorCode_) {
inputText =inputText_;
colorCode=colorCode_;
TextEditorDialogFragment fragment = new TextEditorDialogFragment();
fragment.show(appCompatActivity.getSupportFragmentManager(), TAG);
return fragment;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.add_text_dialog, container, false);
}
}

Base Activity extending Activity ViewBinding Crash

I have Base Activity extends My all other activities. The Base Activity has features like toolbar, progress bar, and other logic methods that are similar across the activity
After Migrating from ButterKnife to ViewBinding The child activity that is extended from the parent base is not able to access the methods in it And the app gets crashed.
Below is Base Activity Code
pubic class BaseActivity extends AppCompatActivity {
private ActivityBaseBinding activityBaseBinding;
#Override
public void setContentView(int layoutResID) {
activityBaseBinding = ActivityBaseBinding.inflate(getLayoutInflater());
View view = getLayoutInflater().inflate(layoutResID, activityBaseBinding.container, false);
if (layoutResID == R.layout.activity_home) {
activityBaseBinding.toolbarTitle.setVisibility(View.GONE);
} else if (layoutResID == R.layout.activity_my_account) {
activityBaseBinding.toolbarTitle.setVisibility(View.VISIBLE);
} else {
activityBaseBinding.toolbarTitle.setVisibility(View.GONE);
}
activityBaseBinding.container.addView(view);
setContentView(activityBaseBinding.getRoot());
activityBaseBinding.imgBackArrow.setOnClickListener(v -> onBackPressed());
}
If I try to access the toolbar in the base from the child activity this is the error I get
java.lang.RuntimeException: Unable to start activity
ComponentInfo{com.iowave.scheduler/com.example.myaccount.views.MyAccount}:
java.lang.NullPointerException: Attempt to read from field
'com.google.android.material.appbar.MaterialToolbar
com.example.databinding.ActivityBaseBinding.toolbarTitle' on
a null object reference
This is the code of my child activity
public class MyAccount extends BaseActivity implements MyAccountImpl {
private NavController navController;
private ActivityMyAccountBinding activityMyAccountBinding;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityMyAccountBinding = ActivityMyAccountBinding.inflate(getLayoutInflater());
View view = activityMyAccountBinding.getRoot();
setContentView(view);
}
}
From what I understand the view binding is only binding the child view and removing all other bindings from the background.
Use this approach for base activity patterns.
abstract class BaseActivity1<Binding extends ViewBinding> extends AppCompatActivity {
Binding binding;
abstract Binding getBinding();
void initBinding(){
binding=getBinding();
}
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.initBinding();
setContentView(binding.getRoot());
}
}
then
class MainActivity extends BaseActivity1<ActivityMainBinding>{
#Override
ActivityMainBinding getBinding(){
return ActivityMainBinding.inflate(getLayoutInflater());
}
}
This could be solve your problem:
I just migrated the project from Butterknife to ViewBinding and face problems related implementing encapsulated activities view into base activity' container, too. Before implementing ViewBinding, I was using getLayoutInflater().inflate(layoutResID, activityContainer, true); where layoutResID was id of encapsulated activities' xml layout (R.layout.activity_main) in BaseActivity so I could inflate encapsulated activities' xml inside activityContainer which is corresponding to FrameLayout inside BaseActivity's xml. But then I figure out that, with ViewBinding, there is no ViewGroup id (xml) but View. So I couldn't use getLayoutInflater().inflate(layoutResID, activityContainer, true); because layoutResID is a ViewGroup id, not View. Here is how I solved.:
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
private var _binding: VB? = null
val childActivityBinding: VB
get() = _binding as VB
abstract fun inflateLayout(
parent: FrameLayout,
inflater: LayoutInflater
): VB
lateinit var baseActivityBinding: ActivityBaseBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
baseActivityBinding = ActivityBaseBinding.inflate(layoutInflater)
_binding = inflateLayout(baseActivityBinding.root.layout_container, layoutInflater)
val coordinatorLayout = baseActivityBinding.root
.
.// some base activity processes like setting Snackbar etc.
val snack = coordinatorLayout.snack
.
super.setContentView(coordinatorLayout)
}
}
Here is Main Activity: [ in Java :) ]
public class MainActivity extends BaseActivity<ActivityMainBinding> {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#NonNull
#Override
public ActivityMainBinding inflateLayout(#NonNull FrameLayout parent, #NonNull LayoutInflater inflater) {
return ActivityMainBinding.inflate(inflater, parent, true);
}
}
With this approach, I was able to inflate the encapsulated activities view inside base activity like a subview.

How to use Picasso in onCreateView?

I have problem with Picasso code. In my fragment_home (fragment of navigation bar) is ImageView where I want to put image from "image.com" URL addres.
Picasso code look like that
Picasso.get().load("image.com")
.resize(300,200)
.centerInside()
.into(photo);
I cant write that into my HomeFragmentActivity where code suppose to be because findViewById isn't "working".
HomeFragmentActivity > Place where code have to be
public class HomeFragment extends Fragment {
private ImageView photo;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_home /*in this layout is ImageView where Picasso is inserting image from URL addres*/) , container, false);
}
}
When code is somewhere else (for example in MainActivity in onCreate method) app will crash. Please help. Thanks.
Write below you'r suggests, maybe Picasso code don't must be in HomeFragmentActivity ?
You can declare it like this :
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_home, container, false);
photo = view.findViewById(R.id.photo);
Picasso.with(context).load("url")
.resize(300,200)
.centerInside()
.into(photo);
return view;
}
or
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
photo = view.findViewById(R.id.photo);
Picasso.with(context).load("url")
.resize(300,200)
.centerInside()
.into(photo);
}

Android fragment navigation without button click

I am using a "Drawer Navigation" project using fragments and so far this method works:
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Navigation.findNavController(view).navigate(R.id.nav_gallery);
}
});
But I want to use the navigation method inside a fragment class calling a function like this:
boolean b;
public void fragmentNavigation() {
if (b) {
Navigation.findNavController(getView()).navigate(R.id.nav_gallery);
}
}
I am a newbie using the navigation architecture and I still don't know if I need to declare some type of action listener for that function or how to make it work.
You can do that, but be careful of :
using getView() is Nullable; so you must make sure that your fragment has already created the view.
You can solve this by a couple of ways
First: Overriding onViewCreated() which has a nonNull view argument
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
fragmentNavigation(view);
}
boolean b;
public void fragmentNavigation(View view) {
if (b) {
Navigation.findNavController(view).navigate(R.id.nav_gallery);
}
}
Second: Create a View field in the fragment class, and set it within onCreateView()
View view;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.main_fragment, container, false);
return view;
}
then always call fragmentNavigation(view); on that view field.
Your fragment is hosted by the NavHostFragment of the Navigation Graph; so that you can avoid potential IllegalStateException

onCreateView() method asking for super class call

I have extended fragmentActivity class , as far as I read onCreateView is available in lifecycle of fragmentActivity but its not supposed to be calling super class , but its giving me error in #override asking me to call super class
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
checkLocationPermission();
}
// Initializing
MarkerPoints = new ArrayList<>();
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
#Override --> error Here
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_maps,
container, false);
Button button = (Button) view.findViewById(R.id.reportButton);
button.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
Toast.makeText(getApplicationContext(),"Report Button Works",Toast.LENGTH_SHORT).show();
}
});
return view;
}
#Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
return super.onCreateView(parent, name, context, attrs);
}
Not
#Override --> error Here
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
You need you Fragments;
Step 1 - You need create one Fragment.
public class ExampleFragment extends Fragment {
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
//Create the view from XML layout.
final View view = inflater.inflate(R.layout.fragment_example, null);
//Perform additional configuration on layout components here.
return view;
}
}
Step 2 - Create Fragment:
public class Fragment1 extends Fragment{
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.YOUR_LAYOUT, container, false);
}}
Step 3 - Create FragmentPagerAdapter
public class PagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragmentList;
private Fragment1 fragment1;
public PagerAdapter(FragmentManager fm) {
super(fm);
this.fragment1 = new Fragment1();
this.fragmentList = new ArrayList<>();
this.fragmentList.add(this.fragment1);
}
#Override
public Fragment getItem(int position) {
//Get fragment in list position
return fragmentList.get(position);
}
#Override
public int getCount() {
//return size of fragments list.
return fragmentList.size();
}}
Step 4 - Add FragmentPagerAdapter in your FragmentActivity:
this._viewPager.setAdapter(new PagerAdapter(this.getSupportFragmentManager()))
FragmentActivity doesn't actually have a method called onCreateView(LayoutInflater, ViewGroup, Bundle), I think you're confusing it with Fragment.onCreateView(LayoutInflater, ViewGroup, Bundle) for which the default implementation only returns null in which case you don't actually need to call the super method.
FragmentActivity can act as a host for Fragments provided by the support library (regular Activity can only host android.app.Fragment). In your code you're using the SupportMapFragment which extends Fragment and overrides Fragment.onCreateView(LayoutInflater, ViewGroup, Bundle) to provide its view to the hosting Activity.
For an Activity, you'd typically just set your button click listeners in onCreate after calling setContentView.
You should try overriding onCreate and changing it from protected to public.
Plus, either get rid of setContentView(R.layout.activity_maps); or change it to getActivity().setContentView(R.layout.about_main_layout);
setContentView isn't availible only in a fragment Activity's java it must have getActivity(). in front of it.
Also, you need a blank public constructor, it's required, public BlankFragment() {
// Required empty public constructor
}
Finally, to my knowledge onCreateView is fine, it's what's around it, so please consider the above posibilites.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().setContentView(R.layout.about_main_layout);
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
checkLocationPermission();
}
// Initializing
MarkerPoints = new ArrayList<>();
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this); }
FragmentActivity is used to hold a Fragment prior to API 3.0. If you're targeting an app prior to Honeycomb then you should use FragmentActivity but if you're targeting an app after Honeycomb then you should use an Activity. And remember that the FragmentActivity holds a Fragment, which means that you should attach a Fragment and call OnCreateView() and other fragment methods on the Fragment and not on FragmentActivity.
The way to attach a Fragment to FragmentActivity is
android.support.v4.app.FragmentManager fm = myFragmentActivity.getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(CONTENT_VIEW_ID, myFragment).commit();
Check out this excellent SO post explaining the difference between FragmentActivity and Fragments
Difference between Fragment And FragmentActivity

Categories