I'm using Kotlin in android studio to make an app.
In my main activity I have a function changeText() that changes the text of a textbox.
I have a class that I'm implementing called VerificationListener() that when created will do things then call onVerified(), however I cannot call changeText from onVerified, is there a way to do so? The example I'm working off of is in Java and does it.
Example I'm working off of
public void onVerified() {
mIsVerified = true;
Log.d(TAG, "Verified!");
hideProgressAndShowMessage(R.string.verified);
showCompleted();}
Above is within the class, below is just sitting in the activity
private void showCompleted() {
ImageView checkMark = (ImageView) findViewById(R.id.checkmarkImage);
checkMark.setVisibility(View.VISIBLE);
}
If by "I cannot call changeText from onVerified" you mean that you have a VerificationListener as a separate standalone class and from that class you cannot call methods on the Activity, you should either a) make the VerificationListener an inner class of the Activity, b) pass your activity into the VerificationListener when it's created (be aware of the lifecycle) c) implement some messaging solution (broadcast receiver, startActivity + onIntent(), observable, or even an event bus (not advisable). Here is a sample implementation for b:
class MyActivity : Activity(), VerificationListener.OnVerifiedCallback {
fun onVerified() {
changeText()
}
override fun onCreate(state: Bundle) {
super.onCreate(state)
VerificationListener(this).doStuff()
}
}
class VerificationListener(internal var callback: OnVerifiedCallback) {
interface OnVerifiedCallback {
fun onVerified()
}
fun whenSomethingGetsVerified() {
doThings()
callback.onVerified()
}
}
EDIT: forgot you are using Kotlin, changed to Kotlin implementation
You can't access the UI from a background thread, Kotlin or not. You have to run this on the UI thread:
runOnUiThread {
val checkMark: ImageView = findViewById(R.id.checkmarkImage)
checkMark.visibility = View.VISIBLE
}
Related
I have a question. I have 2 activities in each I initialize the broadcast receiver to obtain internet connection changes. After changes are received, I notify the user about it by displaying some text on the screen.
In order not to write the code several times, I created BroadcastActivity that inherits from AppCompatActivity.
Now I pass views with super() to BroadcastActivity and manipulate with views (Change texts, visibilities etc.).
Question is: Is it best practice to do so or is there a more elegant solution? And if it can lead to memory leaks?
Thank you for your help!
Example of BroadcastActivity
open class BroadcastActivity : AppCompatActivity(), OnConnectionChangeListener {
//some code
}
And example of activity (Secondary activity structure similar to this)
public class MainActivity extends BroadcastActivity{
//some code
}
P.S. BroadcastActivity written on Kotlin and MainActivity on Java.
P.S. 2 At this moment I can't switch to single activity and looking for temporary solution.
To further elaborate on my comment, here's something "better" (again, in my personal opinion) ...
ActivityKtx.kt:
fun Activity.applyBroadcastReceiver(broadcastReceiver: BroadcastReceiver) {
lifecycle.addObserver(
BroadcastReceiverObserver(
BroadcastReceiverConfigurationImpl(broadcastReceiver, this)
)
)
}
BroadcastReceiverObserver.kt:
/**
* Class implementing [DefaultLifecycleObserver] which is in charge of delegating setting the
* [BroadcastReceiver]'s registration status.
* Registers in [Activity.onResume] and unregisters in [Activity.onPause]
*/
class BroadcastReceiverObserver(
val broadcastReceiverConfig: BroadcastReceiverConfiguration
) : DefaultLifecycleObserver {
var isBroadcastReceiverRegistered = false
override fun onResume(owner: LifecycleOwner) {
super.onResume(owner)
if (isBroadcastReceiverRegistered.not()) {
broadcastReceiverConfig.registerBroadcastReceiver()
isBroadcastReceiverRegistered = true
}
}
override fun onStop(owner: LifecycleOwner) {
if (isBroadcastReceiverRegistered) {
isBroadcastReceiverRegistered = false
broadcastReceiverConfig.unregisterBroadcastReceiver()
}
super.onStop(owner)
}
// add same things for onStart() and onPause()
}
/**
* Base class for handling a [BroadcastReceiver] registration status
*/
abstract class BroadcastReceiverConfiguration {
abstract fun registerBroadcastReceiver()
abstract fun unregisterBroadcastReceiver()
}
/**
* Base class for handling the broadcast receiver
*
* #see [BroadcastReceiverObserver]
*/
class BroadcastReceiverConfigurationImpl constructor(
private val broadcastReceiver: BroadcastReceiver,
private val hostActivity: Activity
) : BroadcastReceiverConfiguration() {
override fun registerBroadcastReceiver() {
//ConnectivityManager.CONNECTIVITY_ACTION - deprecated
hostActivity.registerReceiver(broadcastReceiver, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))
}
override fun unregisterBroadcastReceiver() {
hostActivity.unregisterReceiver(broadcastReceiver)
}
}
then just use it like this:
SomeActivity.kt:
class SomeActivity extends AppCompatActivity {
override fun onCreate() {
super.onCreate()
applyBroadcastReceiver(myBroadcastReceiver)
}
and voila. zero need for subclassing the Activity / AppCompatActivity / MyBaseActivity or whatever.
i have an activity where i have a text view. I am also using an sdk which has some events.
Within on of these events i need to display some data at the TextView.
The events are in an external class and i got them via some interfaces.
The interfaces iam implementing within the Activity.
I am using Handler and Message to set the text into the TextView within the implementation of the interface.
I would like to know if there is another way to set the text within the the interfaces except Handler and Message.
Here is that code:
public class MyActivity extends AppCompatActivity {
private TextView nameTextView;
// some code
// implementing interfaces of external class
Service.ServiceEvents events = new Service.ServiceEvents() {
#Override
public void onSomeEvent(String name) {
nameTextView.setText(name); // not working
Message m = myHandler.obtainMessage(0, nameTextView);
m.sendToTarget();
}
Handler myHandler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message message) {
String s = message.obj.toString();
nameTextView.setText(s);
}
};
}
} // end of activity
Is there another way to set the text in the TextView?
Please Help.
What to use
You can do it by using an interface.
How to implement
Create a new interface and give it a name whatever u like.(Here it is SampleInterface).
Add some void methods to like this
void onSomeEvent(String name);
Create a object of it in the class where you get the data from the sdk and add this listener in its constructor. Something like this
public YourJavaClassName(SampleInterface sampleInterface) {
this.sampleInterface= sampleInterface;
}
When you receive an update in the sdk, you can call it like this
void onDataReceived(String name){// I don't know how you get the value from the sdk so I wrote this
sampleInterface.onSomeEvent(name);
}
You are done with implementing it in the sdk receiver class. Now you need to add to the main activity to receive and update data.
Create the object of the class where you receive the data from the sdk.Like this
MySDKReceiverClass mysdkreceiverclass;
mysdkreceiverclass = new MySDKReceiverClass(this);
Implement the interface in the activity and there you ge the values and you can set it in the textview.
Note: You need not add any runnable or handler or anything.Whenever there is a value change, the listener is called and the value is set.
I am trying to use filter in Fragment and implementing the dialog fragment.
This is the class that I am using
public class HomeFragment extends Fragment implements
FilterDialogFragment.FilterListener,
PostAdapter2.OnPostSelectedListener{ detail code }
this the dialogfragment based class for spinner choosing options
public class FilterDialogFragment extends DialogFragment
this method is called upon clicking the filter button, which pops up dialog for spinner options of the filter
Declared
private FilterDialogFragment mFilterDialog;
in onCreateView
mFilterDialog = new FilterDialogFragment();
Method to call
public void onFilterClicked(){
mFilterDialog.show(getSupportFragmentManager(), FilterDialogFragment.TAG);
}
after this upon selecting the spinner option and clicking apply this method is called in which mFilterListener is null which should not be the case
public interface FilterListener {
void onFilter(Filters filters);
}
FilterListener mFilterListener;
public void onSearchClicked() {
Log.d("Message", String.valueOf(mFilterListener));
if (mFilterListener != null) {
Log.d("Message", "New 55555");
mFilterListener.onFilter(getFilters());
}
dismiss();
}
please assist me to solve this problem. if anymore details are required please let me know
attach method in FilterDialogFragement
public void onAttach(Context context) {
super.onAttach(context);
Log.d("Message", "New 6666666");
Log.d("Message", String.valueOf(mFilterListener));
if (context instanceof FilterListener) {
// Log.d("Message", String.valueOf(mFilterListener));
mFilterListener = (FilterListener) context;
}
}
You are attempting to mimic this library implementation: Friendly Eats.
However, you do not copy it wholesale, mainly in that you choose to use HomeFragment which implements FilterDialogFragment.FilterListener to launch FilterDialogFragment, rather than the library's MainActivity. This is the cause of your null pointer.
This is due to how getSupportFragmentManager() works. If you look at Android's documentation for this, you will see it says
Return the FragmentManager for interacting with fragments associated with this activity. (My Bolding)
When you call mFilterDialog.show(getSupportFragmentManager(), FilterDialogFragment.TAG); inside HomeFragment, you are actually calling whatever Activity that is the parent of HomeFragment to launch the new FilterDialogFragment. You could double check this by checking if, in onAttach(Context context) inside HomeFragment, if context instanceof HomeFragment. I do not think it will return true.
To solve this, and without changing your use of HomeFragment, you could simply pass an instance of HomeFragment itself, or a separate implementation of FilterDialogFragment.FilterListener (which I'd prefer if you do not need to use anything from HomeFragment other than the listener) to your FilterDialogFragment instance on creation or before you launch it.
For example, you could create a public setter like so:
private FilterListener mFilterListener;
public void setFilterListener(FilterListener filterListener){
mFilterListener = filterListener;
}
and then in your HomeFragment onCreateView(), you do this:
mFilterDialog = new FilterDialogFragment();
//Or preferably, an anonymous/named implementing instance of the interface only.
mFilterDialog.setFilterListener(this);
Doing so would not rely on the Android framework to provide the initialisation of your field, and does not require you to either change your Activity or HomeFragment you are currently using.
I assume, that u didnt set the listener in a mFilterDialog, so thats why its null
I used the anko library to create a login view.
class SingInView : AnkoComponent<SingleInActivity> {
override fun createView(ui: AnkoContext<SingleInActivity>) = with(ui) {
verticalLayout {
lparams(width = matchParent, height = matchParent)
textView("Member Login")
editText {
hint = "E-mail"
}
editText {
hint = "PassWord"
}
button("Login")
}
}
}
and SingleInActivity.kt
class SingleInActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState)
SingInView().setContentView(this)
and MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startActivity(new Intent(this, SingInView.class));
finish();
}
}
current My app MainActivity -> SingleInActivity -> SingInView .
of course it can be made simply.
but there is a condition
1. MainActivity is java (kotlin prohibition)
2. use only MainActivity, SingInView.
How to solve this problem?
How to call the Anko class directly from a Java class
If you dig through the Anko source code you'll quickly find this:
interface AnkoComponent<in T> {
fun createView(ui: AnkoContext<T>): View
}
And from the wiki (where MyActivityUI is the component): MyActivityUI().setContentView(this). Now, the AnkoComponent is just an interface and the setContentView method is an extension function that returns createView.
Anyways, the setContentView extension function passes the last variable of the AnkoContextImpl as true. The last variable is whether or not to actually set the content view, which is the reason the activity is passed in the first place.
TL;DR (and possibly more sensible summary of my point):
The component is not an Activity
The setContentView method is not a replacement for setContentView in an Activity; just a wrapper for it.
And since it isn't an activity, you can't use an intent into it. And, as a result of that, you cannot use it standalone. You need an activity. Now, you can of course use the regular approach, but there's also another way. Since the AnkoComponent itself doesn't have any fields, it can be serialized without much trouble. Just to clarify: some fields can be serialized even if it isn't serializable (all though some classes like Context cannot be serialized). Anyways, you create an activity:
class AnkoComponentActivity : AppCompatActivity(){//Can be a regular Activity too
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState);
val component = intent.getSerializableExtra("uiComponent") as AnkoComponent<AnkoComponentActivity>//The type has to match this activity, or setContentView won't allow passing `this`
component.setContentView(this)//The context of the activity doesn't get passed until this point, which enables the use of this system.
}
}
Or it's equivalent in Java:
public class AnkoComponentActivity extends AppCompatActivity {
public void onCreate(Bundle sis){
super.onCreate(sis);
AnkoComponent<AnkoComponentActivity> component = (AnkoComponent<AnkoComponentActivity>) getIntent().getSerializableExtra("uiComponent");
org.jetbrains.anko.AnkoContextKt.setContentView(component, this);//For reference, this is how you call Kotlin extension functions from Java
}
}
Note that any UI component sent to this class has to be declared with <AnkoComponentActivity>. In addition, the components have to implement Serializable. Otherwise they can't be passed through the Bundle. Alternatively, you can use ints or Strings as identifiers and use the value to pick which AnkoComponent to show.
All though, the absolutely easiest way is just creating one activity per component.
TL;DR: AnkoComponent is not an Activity, meaning you can't use intents into it. You have to use an Activity, but using Serializable enables you to pass the component through a bundle to an Activity made for manual creation of multiple AnkoComponents without specifying specific types.
I need some pointers on doing the following:
lets say i have 10/20 (number doesn't matter) of activities.
each of these activities has a textview that should work like a counter.
each of these activities has a button to go to the next activity.
this counter starts when the app is launched, and increment itself every second.
So what i did so far is:
have in my main activity a method that instantiate a class that extends Thread.
In that class in the run() method, i increment a variable when a second passes.
Now i'm stuck on what i should do next. Any pointers would be appreciated thanks.
Edit: i need a way to communicate from inside the run method, to whichever activity is now currently on screen, to update its textview.
Just a bit of theory here for standard Object Oriented Programming : stick to the recommended principles like Loose Coupling which makes your project code less tied to each other. You can read more on that later.
Now, using Events, you can setup a system that is synonymous with the natural Publisher/Subscriber design pattern. Like this:
The activity that needs to notify the other activities is called Publisher and the other activities that need to be notified are called Subscribers.
From here:
There are already built and tested libraries to do Events in android. Like my favorite EventBus.
Step 1 Add this line to your app-level build.gradle file:
compile 'org.greenrobot:eventbus:3.0.0'
Then create a simple Plain Old Java Object aka POJO class like this:
public class UpdateTextViewEvent{
private String textToShow;
public UpdateTextViewEvent(String text){
this.textToShow = text;
}
//add your public getters and setters here
}
Step 2 Notify others:
When you want to notify anyone of the changes, you simply called this method:
EventBus.getDefault().post(new UpdateTextViewEvent("Some new Text"));
Step 3 Receive notifications
For those who want to be notified of this event, simply do this:
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
NOTE: to actually handle the event:
#Subscribe
public void onEvent(UpdateTextViewEvent event){
String text = event.getTextToShow();
//now you can show by setting accordingly on the TextView;
}
This is so much easier to do, do decouple your code by eliminating static references in your different activities
I hope this helps! Good luck!
make that Textview in second class as
public static Textview text;
and call it in main activity as
SecondActivity obj=new SecondActivity();
obj.text.settext("");
You can create one another activity e.g. BaseActivity extend with Activity class and your all 10/20 activity extends with created BaseActivity Class.
You can use your textview with protected access specifiers.
What you need to do is inside the counter class, create an a method and passed in a TextView as the parameter. Then create an int variable and set the counter as the instance:
Like this
public static class Counter extends Thread{
private static int x;
#Override
public void run(){
x = counter;
}
public void setCounter(TextView tv){
tv.setText(String.valueOf(x));
}
}
Now call this method setCounter(TextView) in all the activity's onCreate() method you'll like to display the counter, and passed in your the layout TextView as the argument. Like this
...
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState):
....
TextView cTextView = (TextView)findViewById(R.id.texT1);
Counter c = new Counter();
c.setCounter(cTextView);
}