Whats the efficient way to add Navigation Drawer on all the activities? I don't want to repeat the code for Navigation Drawer in all the activities and their layouts. Is it possible somehow to add Nav. Drawer in BaseActivity(custom class) and then every other activity will extend BaseActivity inorder to have the Navigation Drawer ?
Is it possible somehow to add Nav. Drawer in BaseActivity(custom class) and then every other activity will extend BaseActivity inorder to have the Navigation Drawer ?
Yes this is definitly the cleanest way to go.
public BaseActivity extends Activity {
#Override
protected void onCreate()
super.onCreate(); // calls Activity.onCreate()
// setup your Navigation Drawer
}
public FirstActivity extends BaseActivity {
#Override
protected void onCreate()
super.onCreate(); // will call the BaseActivitiy.onCreate()
// do something in the FirstActivity
}
public SecondActivity extends BaseActivity {
#Override
protected void onCreate()
super.onCreate(); // will call the BaseActivitiy.onCreate()
// do something in the SecondActivity
}
The "hard work" will be the layouts. Have one baseLayout for the BaseActivity with a place holder for the Content View (the visible part of the Activities). For all other Activitys use this layout and include your Content View.
Related
I am making an Android application which has one activity and many fragments. The activity contains a bottom app bar and that bottom bar has a navigation icon in it. Like this:
<com.google.android.material.bottomappbar.BottomAppBar
android:id="#+id/bottom_appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:backgroundTint="#color/colorbottomappbar"
app:fabAlignmentMode="center"
app:navigationIcon="#drawable/ic_menu_green_24dp">
</com.google.android.material.bottomappbar.BottomAppBar>
This navigation menu icon will be shown in every fragment. However, in some fragments I want to change that navigation icon in the bottom app bar to back button/icon. How can I achieve this? Also, currently I handle the navigation icon click in the main activity. How can I handle the click in the case of the back icon? How will it know what the current fragment is and how can I determine which fragment the back icon leads to?
If you look at the documentation, you'll see that BottomAppBar extends from Toolbar and it has an inherited method called setNavigationIcon(int res).
You can implement an interface that your main Activity implements like so:
interface FramentChangedListener {
void onFragmentChanged(int type);
}
Your activity would do something like this:
public class MainActivity extends Activity implements FragmentChangedListener {
// This will keep track of what is currently shown
private int current = 0;
#Override
public void onFragmentChanged(int type) {
if (type == FirstFragment.SOME_TYPE) {
// Update the current fragment value, we're associating each fragment
// with an int value.
current = type;
bottomAppBar.setNavigationIcon(R.drawable.your_back_icon);
}
}
...
}
In your fragment you would do something like this:
public class FirstFragment extends Fragment {
private FragmentChangedListener listener;
public static final int SOME_TYPE = 1;
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceOf FragmentChangedListener) {
// context in this case is your activity, which implements FragmentChangedListener
listener = (FragmentChangedListener) context;
// You can call the listener now
listener.onFragmentChanged(SOME_TYPE);
}
}
}
In your Activity, add a listener to the BottomAppBar via setNavigationOnClickListener and whenever you receive the navigation icon event, you can check against the current value that we defined.
I'm trying to reduce the double effort of using the duplicate code. I searched on the google but didn't found about it that how we can reduce the duplication of code in android. May be this question could be stupid but I want the clarification.
1) The first thing I want to ask is that how we can reuse the same code in the multiple activity which are being using in overriden method as onBackPressed() onOptionsItemSelected() and so on. Here is the code which I'm currently writing in the onBackPressed() method.
#Override
public void onBackPressed(){
if(this.mDrawerLayout.isDrawerOpen(GravityCompat.START)){
this.mDrawerLayout.closeDrawer(GravityCompat.START);
}else{
super.onBackPressed();
}
}
For this approache I get the suggestion from this question to make the base activity then override this method after that extends other activities from that BaseActivity. But how I can pass the mDrawerLayout field in the BaseActivity? how I can use findViewById() on that base activity to access the xml widgets to access in the overriden method as currently using mDrawerLayout layout.
Example in code.
public BaseActivity extends AppCompatActivity{
private DrawerLayout mDrawerLayout; // how to initialize it? where to call findViewById?
#Override
public void onBackPressed(){
if(this.mDrawerLayout.isDrawerOpen(GravityCompat.START)){
this.mDrawerLayout.closeDrawer(GravityCompat.START);
}else{
super.onBackPressed();
}
}
}
The same question for the onOptionsItemSelected() mean menu item actions.
2) The second duplication I'm facing about hundred of time is startActivity() I have to write Intent then add the extra data if required than use that Intent. so about 2 to 3 lines I have to write again and again.
3) This thing is about the XML, I'm using the value 5dp or 10dp or 10sp or other dimen values in the XML file which are repeating a lot of time. So I want to ask is this approach will be Ok?
<dimen name="ten_dp">10dp</dimen>
android:layout_margin="#dimen/ten_dp"
Mean declare the dp value then use that in the XML.
Edited:
4) This problem I have faced now. I'm using the same toolbar in all activities and after adding it in XML layout I have to write this code.
private void setupToolbar(){
setSupportActionBar(mainToolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeAsUpIndicator(R.drawable.ic_menu);
}
}
But I have to re-write/copy the code in all activities. What is the solution of this problem?
1) For your first question, you'd have to have mDrawerLayout in your BaseActivity, then you'd attach your child activity's DrawerLayout into that field on the child activity's onCreate():
super.mDrawerLayout = findViewById(R.id.drawer_layout)
You can also do that for other #Override methods, such as onOptionsItemSelected().
Example based on your BaseActivity class:
public class MainActivity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDrawerLayout = findViewById(R.id.drawer_layout);
}
}
Also, make your BaseAcitivity abstract to avoid the "add to manifest warning":
public abstract class BaseActivity extends AppCompatActivity
2) As for your second question, I'd say that in your BaseActivity you could have a public method called getIntent() which would return a common Intent object. You'd then call that method from your child Activity.
You can pretty much have any method with common functionalities on your BaseActivity.
3) From my personal experience, it is counterproductive to declare resource values that does not represent the purpose of itself, such as yours: <dimen name="ten_dp">10dp</dimen>. I'd rather name it based on a representative use, such as <dimen name="activity_margin">10dp</dimen> or whatever.
It is a good practice indeed to have all your hardcoded values (strings, dimens, colors, etc) on your XML resources, you just need to give them significant names (treat them as variable names).
Edit:
4) Add that method into your BaseActivity and pass the Toolbar as a param:
private void setupToolbar(Toolbar mainToolbar){
setSupportActionBar(mainToolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeAsUpIndicator(R.drawable.ic_menu);
}
}
Then, you can call it from your MainActivity as:
Toolbar mainToolbar = findViewById(R.id.main_toolbar);
setupToolbar(mainToolbar);
1- How to get reference to the drawer:
If all activities have drawer in its xml, and with the same Id, then you can use findViewById in BaseActivity.
protected DrawerLayout drawerLayout;
protected void initNavDrawer() {
drawerLayout = findViewById(R.id.drawer);
}
The BaseActivity is actually the current running activity, So findViewById is totally fine but make sure you have the drawer in all activity, else you will face NullPointerException
2- For starting activity, In each activity I add a static method which opens the activity
public static void openForEdit(#NonNull Activity context, #NonNull Order order, int requestCode) {
Intent editOrderIntent = new Intent(context, EditOrderActivity.class);
editOrderIntent.putExtra(ORDER_TAG, order);
editOrderIntent.putExtra(EDIT_TAG, true);
context.startActivityForResult(editOrderIntent, requestCode);
}
You can make more than one method if needed.
Then wherever you want to open the activity just call the static method
MyActivity.open(//passing parameters);
3- About the third point I didn't get exactly your point, But just for clarification: You need to create new xml file called dimens.xml, then you declare your dimens
4- You can put this method in BaseActivity and call it from onCreate in BaseActivity
How can I define a different Statusbar and Actionbar color for each fragment ?
At the moment.
How it should look.
First of all, I would highly recommend you to migrate to new approach - Toolbar. It is much more flexible and you can customize it as plain View.
About your question.
You can just get ActionBar object and setBackground programatically.
Here is short example
ActionBar bar = getActionBar();
bar.setBackgroundDrawable(new ColorDrawable("COLOR IN HEX 0xFFFF6666 for instance"));
I will show how would I implement this. This is more about architecture and patterns.
Use some base class for Fragment it would be better to have base class for Activity as well. Lets consider
public class BaseFragment extends Fragment
And you Activity class in which your fragment lives.
public class MainActivity extends Activity
And you have to define responsibilities of Activity in this case and create interfaces
In your case to work with ActionBar
Create interface
public interface ActionBarProvider {
void setActionBarColor(ColorDrawable color);
}
Make your activity implement this interface
public class MainActivity extends Activity implements ActionBarProvider {
public void setActionBarColo(ColorDrawable color) {
ActionBar bar = getActionBar();
bar.setBackgroundDrawable(color));
}
}
And finally in BaseFragment in onAttach
public void onAttach(Context context) {
super.onAttach(context);
mActionBarProvider = (ActionBarProvider) context;
}
Make mActionBarProvider variable protected and make each fragment extend BaseFragment and you can change action bar color from any fragment like this mActionBarProvider.setActionBarColor(new ColorDrawable());
Hope this helps.
I have created a baseactivity with a navigation drawer and searchview. I am trying to extend this in other activities, but also have different content. However if I extend the base activity, and try to set a different content view, I don't get the navigation drawer/search view.
How do i extend the baseactivity, but still customize the ui for each activity?
Thanks for you help!
The best way to do this would be to use Fragments. However, to use multiple activities, extend the base activity and then use setContentView() with the layout for that activity. Ensure that the xml for that layout contains the navigation drawer layout too.
Sounds like you want to do something like this. You'll see that any of the Activities that inherit from this one and call setContentView will now be adding their content to the content frame in your already inflated navigation layout.
public class BaseActivity extends FragmentActivity
{
private FrameLayout mContentFrame;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// notice we call super.setContentView here
super.setContentView(R.layout.activity_main);
// and other nav stuff here
mContentFrame = findViewById(R.id.content_frame);
}
#Override
public void setContentView(int layoutResID)
{
setContentView(getLayoutInflater().inflate(layoutResID, mContentFrame, false));
}
#Override
public void setContentView(View view)
{
mContentFrame.removeAllViews();
mContentFrame.addView(view);
}
#Override
public void setContentView(View view, LayoutParams params)
{
mContentFrame.removeAllViews();
addContentView(view, params);
}
#Override
public void addContentView(View view, LayoutParams params)
{
mContentFrame.addView(view, params);
}
}
I have three activities MainActivity, SecondActivity, ThirdActivity
Instead of fragments I'm using activities.
I made a navigation drawer for MainActivity. SecondActivity and ThirdActivity extended MainActivity.
MainActivity[
//NavigationDrawer code
]
SecondActivity Extends MainActivity[
]
ThirdActivity Extends MainActivity[
]
Drawers icon shows up in Second and Third Activity but its not opening up on clicking it.
please help me.
Create a method in your MainActivity as
public void openDrawer()
{
mDrawerLayout.openDrawer(Gravity.LEFT);
}
So you can access it from your any of activity
ThirdActivity extends MainActivity
{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Set button clicki event and just call function of mainActivity
openDrawer();
}
}
You've got three different activities, so you have to add the drawer in the layouts of each of these activities. A more suitable solution would be to use fragments instead of an activity