Android Activity Reusability - java

I've created a reusable xml layout that looks like this
I would like to use same component on different activities , what i want to do is to create BaseActivityClass that extends Activity.
public class BaseActivityClass extends Activity {
int layout_id = R.layout.SomeLayout;
final int menu_button_id = R.id.menuButton;
final int save_button_id = R.id.saveButton;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(layout_id);
Button btn = findViewById(menu_button_id);
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//this functionality will be same on every child class
}
});
}
}
I would like to extend that class as
public class SomeActivityClass extends BaseActivityClass {
int layout_id = R.layout.SomeOtherLayout;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
There is no constructor class for Activity.On intent call only class name is referenced.
And subclass cannot change super class variables(hiding).
I dont want to copy paste same code that is in BaseActivityClass into other classes.
A wrapper class might solve the problem but that seems too sloppy imo.
How can i solve this design issue?
I am free to comment on any idea

Sounds like you want the base class to control the main container layout and allow the derived class to provide a "content" layout. Is that correct? If so, you can use this pattern:
Step 1 - Add a ViewStub to your base layout. Some pseudocode to give you the idea:
<ConstraintLayout>
<!-- Common Stuff -->
<Button id="menu">
<Button id="save">
<!-- "Content" area to be filled by derived classes -->
<ViewStub id="viewStub" />
</ConstraintLayout>
Step 2 - Update your base layout to provide a way to inflate content into the "content area". Some Pseudocode:
public abstract class BaseActivityClass extends Activity {
int layout_id = R.layout.SomeLayout;
final int menu_button_id = R.id.menuButton;
final int save_button_id = R.id.saveButton;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(layout_id);
Button btn = findViewById(menu_button_id);
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//this functionality will be same on every child class
}
});
// Get the ViewStub, set the derived class's content layout on it and inflate it,
// thereby displaying the derived class's view layout within the base class's
// "content area"
ViewStub vs = findViewById(viewStub);
vs.setLayout(getContentLayoutId());
vs.inflate();
}
// Define abstract method that all derived classes must implement to provide
// the id of the layout to show in the "content area"
#LayoutRes
public abstract int getContentLayoutId();
}
Step 3 - Update your derived class to provide the layout you want to display. Some pseudocode:
public class SomeActivityClass extends BaseActivityClass {
int layout_id = R.layout.SomeOtherLayout;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
#LayoutRes
public int getContentLayoutId() { return layout_id; }
}
Now when SomeActivityClass is created, it calls the super class onCreate which inflates your base layout and then asks the derived class for it's main content layout that it shoves into the "content area".
I've used this pattern on my projects and it works quite well.
Another option is to simply pass the layout ID via the super class constructor. If the base Activity is abstract it will never be instantiated and doesn't have to adhere to the zero-arg constructor rule. Only your derived class does. So you can do something like this:
public abstract class BaseActivityClass extends Activity {
private final int mContentLayoutId;
protected BaseActivityClass(int contentLayoutId) {
mContentLayoutId = contentLayoutId;
}
protected void onCreate(Bundle state) {
// Same code to load ViewStub, but use mContentLayoutId instead
}
}
public SomeOtherActivity extends BaseActivity {
public SomeOtherActivity() {
super(R.layout.SomeOtherLayout); // Call super with derived layout
}
}
Hope that helps!

Related

Create generic/base form class regardless if activity or fragment

I'm trying to create a generic/base form regardless if it is an activity or fragment. To make it simple, a Form can submit so:
class BaseFormActivity extends AppCompatActivity {
public abstract void submitForm();
#Override
public void setContentView(int layoutResID) {
ConstraintLayout activityBaseForm = (ConstraintLayout) getLayoutInflater().inflate(R.layout.activity_base_form, null);
FrameLayout frameBaseForm = activityBaseForm.findViewById(R.id.frame_base_form);
getLayoutInflater().inflate(layoutResID, frameBaseForm, true);
findViewById(R.id.btn_submit).setOnClickListener(v -> submitForm()) // for the sake of simplicity, there's a button that will trigger submitForm() method
super.setContentView(activityBaseForm);
}
}
Here, I just include some default layout for a form, and a button for submit that triggers the abstract method submitForm(). But, this is only for android activities. How can I make this also available for fragments without writing a BaseFormFragment? I don't want to repeat default behaviors from activity to the fragment and vice versa.
Consider it as a sample Presenter class which handle your button click and get all form fields and send to server
public class MyPresenter {
private MyPresenterIView iViewInstance;
public MyPresenter(MyPresenterIView iView){
this.iViewInstance=iView;
}
public void onSubmitClick(){
//write your logic here
String fieldOneText=iViewInstance.getFieldOneText();
sendToServer(fieldOneText);
}
private void sendToServer(String stringInfo){
//send info to server
}
}
MyPresenterIView Interface
public interface MyPresenterIView{
String getFieldOneText();
}
And use Presenter in your Activity or Fragment
//implement MyPresenterIView to your Activity or Fragment
public class MyActivity extent SomeActivity implements MyPresenterIView{
private MyPresenter myPresenter;
//in onCreate or onCreateView(if its a fragment) initialize myPresenter
protected void onCreate(..){
myPresenter=new MyPresenter(this);//this will enforce Activity/Fragment to implement IView
}
#Override //comes from MyPresenterIView
public String getFieldOneText(){
return ((EditText)findViewById(R.id.edttext_field_one)).getText().toString().trim();
}
}

Android - accessing var in class in class

I am making a module to include with all my applications. In this module, onCreate is overridden to perform code I like doing in all my apps, like using SupportActionBar. In all my applications, I have stuck to keeping my toolbar element id toolbar. I want the overridden function to access this, without actually giving it the id. I don't want to have to do super.onCreate(savedInstanceState, R.id.toolbar) but super.onCreate(savedInstanceState, R.class) or related, and get the function to get the variable on its own, like:
// In custom parent class
protected void onCreate(Bundle savedInstanceState, Class r) {
super.onCreate(savedInstanceState);
Toolbar t = findViewById(r.getClass("id").getInt("toolbar"));
setSupportActionBar(t);
}
// Main func
// Extends class in which above function resides
#Override
protected void onCreate(Bundle b) {
super.onCreate(b, R.class);
}
In parameter should be object of Resources class. So:
// In custom parent class ( extends android.support.v7.app.AppCompatActivity )
public void doStuff(Resources r) {
Toolbar t = findViewById(r.getClass("id").getInt("toolbar"))
setSupportActionBar(t);
}
// Main func
// Extends class in which above function resides
#Override
protected void onCreate(Bundle b) {
super.onCreate(b);
doStuff(getResources()); //we have Resources
}
but i do not undestand what is purpose of sending R as method parameter. It can be done this way:
// In custom parent class
public void doStuff() {
Toolbar t = findViewById(getResources().getClass("id").getInt("toolbar"))
setSupportActionBar(t);
}
// Main func
// Extends class in which above function resides
#Override
protected void onCreate(Bundle b) {
super.onCreate(b);
doStuff();
}

Android: call a function only on child activity from parent activity

My ParentActivity.java is like this
public class ParentActivity extends Activity{
public void childOnlyMethod(){
Log.d(TAG,"child only method triggered in parent activity");
}
public void startChildActivtityButton(){
startActivity(new Intent(this, ChildActivity.class));
}
public void childOnlyMethodButton(){
childOnlyMethod();
}
}
And my ChildActivity.java is like this
public class ChildActivity extends ParentActivity{
#Override
public void childOnlyMethod(){
Log.d(TAG,"child only method triggered in child activity");
}
}
The problem is when I press childOnlyMethodButton, childOnlyMethod() in both parent and child activity gets invoked I want it to be invoked on child only how can I achieve that?
#Override annotation does nothing, it is only used to tell the compiler and IDE that this method overrides its super class. Non-static methods are associated to objects, not classes. Overriding means completely replace the method in its super class. So if you invoke childOnlyMethod on ChildActivity, only child version will be invoked.
I'm guessing you were actually clicking on the parent activity instance. I don't really get the point why you want to invoke a child method on parent reference. If you can post the real code, I can give you more precise answer.
However, you can try the following code. This example will only invoke child version childOnlyMethod on ChildActivity instance. But it will still invoke parent's childOnlyMethod if you click the button on ParentActivity.
public class ParentActivity extends Activity implements View.OnClickListener {
private static final String TAG = "ParentActivity";
private Button mStartChileButton;
private Button mButton;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mStartChileButton = (Button) findViewById(R.id.startChileButton);
mButton = (Button) findViewById(R.id.button);
mStartChileButton.setOnClickListener(this);
mButton.setOnClickListener(this);
}
public void onClick(View v) {
switch(v.getId()) {
case R.id.R.id.startChileButton:
startActivity(new Intent(this, ChildActivity.class));
break;
case R.id.R.id.button:
default:
childOnlyMethod();
break;
}
}
public void childOnlyMethod() {
Log.d(TAG, "Called from ParentActivity");
}
}
public class ChildActivity extends ParentActivity {
private static final String TAG = "ChildActivity";
#Override
public void childOnlyMethod() {
Log.d(TAG, "Called from ChildActivity");
}
}
If you are extending a parent class, all the methods in the parent class are passed down somewhat to the child class, if you don't want the method in the parent class to be implemented, first start by removing that #Override annotation in the child class above the method. Then use this snipped below, all it does is make the Method in the parent class either private or protected so that it can't be accessed in the child class.
public class ParentActivity extends Activity {
private void childOnlyMethod(){
Log.d(TAG,"child only method triggered in parent activity");
}
//OR
protected void childOnlyMethod(){
Log.d(TAG,"child only method triggered in parent activity");
}
}

In android, when is a context different from "this"?

I am at baby step level of programming on Android (and in Java in general). I do understand that Activity inherits from the Context class. However in every code snippet I have come across, every time a context must be mentionned, it is set to "this".
My question is : when is a context different from "this" ? Could you provide an real life example of context needing to be different from "this"?
Thank you very much.
Typically, you will want to use this when you are "inside" of an Activity. However, when you are using for example a Helper class, the reference this will not work. An example can be something like this:
public class MyActivity extends Activity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
}
}
A case, where you cannot:
public class MyHelper
{
/* some code of yours */
public void lockOrientation(Activity activity)
{
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
}
}
The above code locks the orientation to the current orientation. Notice that you need to supply the method with an Activity parameter, since you cannot use:
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
In the first example, you could use this to achieve this, because you were "inside" of an Activity.
Another type of example, how do you set onClickListener.
First example, when you use this:
public class MyActivity extends Activity implements View.OnClickListener
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Button btn=(Button)findViewById(R.id.mybutton);
btn.setOnClickListener(this);
}
#Override
public void onClick(View v)
{
//handle the click event
}
}
In this example, you can use this because in the first line, we wrote implements View.OnClickListener, so the class inherits from the given interface. Without the implements thingie, you couldn't do it. An example of setting the onClickListener without this:
public class MyActivity extends Activity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Button btn=(Button)findViewById(R.id.mybutton);
btn.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
//handle the click event
}
});
}
}
In the second example, we are defining an Anonymous Inner Class, which will handle the click event of the button. Notice that in this case, our Activity does NOT implements View.OnClickListener.
In Outer Class you directly use "this" reference
In Inner Class Or Abstract Class implementation Or Interface implementation use "classname.this" reference
Example:
class Example{
int number = 0;
public Example(int number){
this.number = number;
}
}
notice that number in the contructor and number in the class are not the same. Altough they have the same name. Saying number = number doesn't make sense. Be using this you can asses number in the class.
For example when you are implementing an OnClickListener the "this" is different.
this is a reference to the current object — the object whose method or constructor is being called.
Inside an Activity's method this can be used as a Context object because Activity inherits from ContextThemeWrapper, which inherits from ContextWrapper, which inherits from Context.
A Fragment on the other hand does not inherit from Context. So to get the Context inside a Fragment you would have to call getActivity() for example.
This applies to any object you are calling this from.
Consider you are inside the OnClick() method of a View.OnClickListener and you want to start an Activity:
button.setOnClickListener (new View.OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent(this, NextActivity.class); // wrong because 'this' is the OnClickListener object
Intent intent = new Intent(CurrentActivity.this, NextActivity.class); // correct because 'this' is the CurrentActivity object
startActivity(intent);
}
});

Abstract class issue

I am creating one parent Activity(Class) and then want to extends this class to another Activity(Class). I have some controls in all the Activities(Classes) so I decide to use Abstract class so that I need not write some common code in all the classes.I created below classes and one of it is abstract class.When I am calling my Welcomepage Activity this will display me a screen with all common controls(Radio buttons in my case).In Abstract class I had set checkedChangedListener listener and in onCheckedChanged() method I am creating a toast but It is not displaying.I am confused in this case.What is the reason to not displaying a toast?.
My Activity(Class) Welcomepage_pillReminder which extends CustomTabsActivity
public class Welcomepage_pillReminder extends CustomTabsActivity
#Override
public void mappingWidgets() {
super.mappingWidgets();
}
#Override
public void addCheckChangedListner() {
super.addCheckChangedListner();
}
CustomActivity
public abstract class CustomTabsActivity extends Activity {
protected RadioButton radHome;
public void mappingWidgets(){
radHome = (RadioButton)findViewById(R.id.radHome);
}
public void addCheckChangedListner() {
radHome.setOnCheckedChangeListener(onCheckedChangeListener);
}
OnCheckedChangeListener onCheckedChangeListener = new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked) {
if(buttonView.getText().equals("Home")) {
Toast.makeText(getApplicationContext(), "Home", 2000).show();
}
}
}
};
}
You can do it like this,
Your CustomActivity.java
public class CustomActivity extends Activity implements OnClickListener{
public void initLayout(Button button){
button.setOnClickListener(this);
}
public void simple_method(){
System.out.println("test in CustomActivity");
}
#Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "Button Clicked in Custom Activity", Toast.LENGTH_LONG).show();
}
}
Activity that extends CustomActivity
CustomClassDemoActivity.java
public class CustomClassDemoActivity extends CustomActivity{
Button button;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
button = (Button) findViewById(R.id.myTextView);
button.setText("This is a Custom Activity Example");
super.initLayout(button);
simple_method();
}
public void simple_method(){
super.simple_method();
System.out.println("test in mainClass");
}
#Override
public void onClick(View v) {
super.onClick(v);
Toast.makeText(getApplicationContext(), "Button Clicked in Main Activity", Toast.LENGTH_LONG).show();
}
}
For your purpose the using of an abstract class dosent make sense.
An abstract class only declares its methods and the concrete class can implement the method then depending on its purpose. It would make sense if you plan to write various classes which extend your activity and each of them would have another implementation purpose for mappingWidgets() and addCheckChangedListner().
In your case a simple class which will be extended would do the job.
Furthermore I see, that you use the #Override annotation in your sub class. With this annotation you override your methods.
At first, please remove the #Override in your methods of the sub class. I guess they will work then.
Maybe also reconsider when usin abstract classes and / or #Override.
I forgot to call function mappingWidgets() and addCheckChangedListner() after calling this two method my code works fine.Thanks all for help me.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.welcome_pillminder);
mappingWidgets();
addCheckChangedListner();
}

Categories

Resources