I'm moving from java to kotlin and I faced with some difficulties which are connected with static method usage at kotlin. I'd like to get access from recyclerView adapter to views at my activity. At Java I did smth like that at adapter:
WriteResponseMess.deleteAttachment(position);
and static method at my activity:
public static void deleteAttachment(int adapterPosition) {
mNames = adapter.getItems();
mNames.remove(adapterPosition);
adapter.updateNames(mNames);
adapter.notifyDataSetChanged();
mNames = adapter.getItems();
}
right now I want to do it via kotlin. The main tack is that I have to delete item of RV and update views. I have read this and this resources and I have done smth like that:
companion object {
#JvmStatic
fun deleteAttachment(position: Int) {
}
}
but I don't have any access to activity variables, so what I have to do in that situation?
A static block can access only static members.
The activity member variables should also be a part of the companion object.
For instance :-
companion object {
var mNames : MutableList<Name> = mutableListOf // is a member variable
......
fun deleteAttachment(position: Int) {
}
}
Related
Hi I have a adapter which displays the list of items and all the functions related to setItems, getCount, notifyDataSetChanged.
adapter also has calls to api's through use cases.
Structure is
Adapter -> UseCase -> Repository -> apiLayer
I am aware that fragemnts and activities should not contains calls to api (usecases in my instance).
So should adapter's have api calls (usecases in my instance)
Thanks
R
It is possible, but from a software design point of view I would not recommend it.
The Adapter's responsibility is to connect your Data with the View and make it available to the ListView/RecyclerView. The Adapter should not have any other dependency (knowledge). This will also make it more robust to changes.
So you should consider that only the Activity/Fragment talks to your Presenter and delegates the results from the Presenter to the Adapter.
This will also make (unit) testing (of the Presenter) more easier.
class YourActivity: Activity() {
private val presenter: YourPresenter = // ...
override fun onCreate() {
val adapter: YourAdapter = YourAdapter()
recyclerView.setAdapter(adapter)
val data = presenter.getData()
adapter.submit(data)
}
}
class YourPresenter(private val useCase: UseCase : Presenter() {
fun getData(): List<Data> {
return useCase.fetchData()
}
}
In my app I have a TabLayout and each of the tabs is represented by a fragment. I have several tables in a database. And for each table I want to have a tab that would display a list of table's contents. To access a database I need to pass in a context but it's only available from the MainActivity. How to access a database instance from each fragment?
Here's some code:
ElectronicsDatabase.java
#Database(entities = {Smartphone.class, Tablet.class,
Laptop.class, VideoGameConsole.class}, version = 1)
public abstract class ElectronicsDatabase extends RoomDatabase {
public abstract SmartphoneDao getSmartphoneDao();
public abstract TabletDao getTabletDao();
public abstract LaptopDao getLaptopDao();
public abstract VideoGameConsoleDao getVideoGameConsoleDao();
private static final String DB_NAME = "products.db";
private static ElectronicsDatabase db;
public static ElectronicsDatabase getInstance(Context context)
{
if (db == null)
{
db =buildDatabaseInstance(context);
}
return db;
}
private static ElectronicsDatabase buildDatabaseInstance(Context context)
{
return Room.databaseBuilder(context, ElectronicsDatabase.class,
DB_NAME).allowMainThreadQueries().build();
}
}
And in the main activity I access it like this:
db = ElectronicsDatabase
.getInstance(getApplicationContext());
In your fragments you can use getActivity() to acccess context of your parent activity.
but i suggest you to use viewModel for accessing to your database.
Try to use ViewModel to access database in any activity or Fragment
ViewModel is a class that is responsible for preparing and managing the data for a UI component (activity or Fragment)
In your case which is needing a Context for DB access via Room, it is better to pass a non-UI Context as to avoid unnecessary information being passed around for no reason leading to possible memory leaks.
You can get access to a non-UI Context which will be called ApplicationContext from your base activity, or your main activity. Simply like this:
Context appContext= getApplicationContext();
Then store it in a Repo class, so you can simply use it anytime you need it again anywhere without worrying about it.
However, if you need a context for something related to drawing on the screen, like inflating an XML for example, then in that case you will need a UI Context as not to lose UI details like your theme for example. In that case you can get the context from inside your fragment using:
getContext() Or getActivity().
I won't go to further details about contexts but,
if you want to learn more about what Context really is you can start from here:
https://medium.freecodecamp.org/mastering-android-context-7055c8478a22
That might be a very late answer, but I believe it might help some in the future:
If you are using multiple fragments and DI, you can create a viewModel for your activity, then inject the desired value into it
#HiltViewModel
class MainActivityViewModel #Inject constructor(
private val repository: PlantsRepository
): ViewModel() {
private val mutableLiveData = MutableLiveData<List<Plant>>()
val liveData: LiveData<List<Plant>> = mutableLiveData // object to observe
init {
viewModelScope.launch {
mutableLiveData.postValue(repository.getAllPlants())
fillExampleData()
}
}
}
(sample for reference)
then you simply use it in your fragments:
class FragmentExampleScreen : Fragment() {
private val sharedViewModel: MainActivityViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
...
sharedViewModel.liveData.observe(viewLifecycleOwner) { idk ->
Log.i("hello", idk.toString())
}
...
}
}
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
}
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 am trying to initialize a class that calls another class that uses AsyncTask. I am using GetDataFromDB gDataFromDB = new GetDataFromDB() but that does not initialize the class, it just gives me access to any static methods in the class. So what do I do to get the onCreate method to run? I have tried using intent but keep getting an error because this is a static class
public class FacadeDataFromDB extends Activity {
static ArrayList<HashMap<String, String>> visitorsList;
private static FacadeDataFromDB dataFromDB;
static boolean accessDB = false;
private FacadeDataFromDB() {
}
public static void initInstance() {
}
public static FacadeDataFromDB getInstance() {
if (dataFromDB == null) {
// Create the instance
dataFromDB = new FacadeDataFromDB();
}
return dataFromDB;
}
public static void setData() {
if (!accessDB) {
GetDataFromDB gDataFromDB = new GetDataFromDB();
accessDB = true;
}
// visitorsList = gDataFromDB.returnInfoFromDB();
}
public static ArrayList<HashMap<String, String>> getVisitorForDay() {
// TODO Auto-generated method stub
setData();
return visitorsList;
}
}
GetDataFromDB is the other class that I am calling. The current class is a static class and uses a singleton because I only want one initialization of the class the gets data from the db. If you have more questions or want me to post code let me know. Thanks
It seems to me that your two classes FacadeDataFromDB GetDataFromDB should not inherit Activity
Activities are made for GUI and user-interaction (I don't see any in your example) and their life-cycle is managed by the framework : you never create them manually with new.
See the android tutorial : https://developer.android.com/guide/components/activities.html and Activity javadoc : https://developer.android.com/reference/android/app/Activity.html.
I'm not sure that you completely understand the Android runtime. You should start Activities using Intent objects, not by creating them with the new keyword as you are. To ensure that your onCreate() method is called within your Activity, you could launch an explicit Intent from some other Activity/Context: Intent intent = new Intent(currentContext, FacadeDataFromDB.class);.
Also, when it comes to Activities, you shouldn't use private constructors. See this post for reasons why.