I need to transmit data from my activity layer to a view (or at least its fragment) that is not a child of AdapterView.
For a ListView, I could do this very easily with its adapter, but I am stuck on how to reproduce this behavior for a non AdapterView widget (for clarity, let's say a TextView).
I don't want to keep a reference to my fragment (or worse, the view) at Activity level.
Any ideas ?
One way to do this is to use java.util.Observable/Observer :
import java.util.Observable;
import java.util.Observer;
public class MyTextView extends View implements Observer{
#Override
public void update(Observable observable, Object data) {
this.setText((String)data);
}
}
Then, you need an Observable class :
import java.util.Observable;
public class MyObservable extends Observable {
public void setText(String text){
notifyObservers(text);
}
}
Activity :
public class MainActivity extends Activity {
TextView tv;
#Override
public void onCreate(Bundle savedInstanceState) {
...
MyObservable mtv = new MyTextView(getApplicationContext());
MyTextViewModel mm = new MyTextViewModel(10);
mm.addObserver(mtv);
mm.setText("test");
// demonstrated in an activity to shorten the sample, but ideally you would
// keep the observer at activity level and manage the view in the fragment
}
}
------------------------------------------------
Another way to do this is through android.database.DataSetObservable to implement a more traditional Adapter like object :
public class CustomAdapter extends DataSetObservable {
String mText;
public String getText() {
return mText;
}
public void setText(String text) {
mText = text;
}
}
You manipulate it like any other adapter at Activity level :
public class MyActivity extends Activity {
private CustomAdapter mCustomAdapter;
#Override
protected void onCreate() {
...
mCustomAdapter = new CustomAdapter();
}
private void initializeFragment (Fragment fragment) {
// this or whatever method you use to setup your fragments
((MyFragment) fragment).setCustomAdapter(mCustomAdapter);
}
private void onDataLoaded (Stg data) {
// callback method invoked when the network thread has finished loading data
mCustomAdapter.setText(data.text);
mCustomAdapter.notifyChanged();
}
Finally, the only thing missing is the link between your fragment and the view :
public class MyFragment extends Fragment {
private CustomAdapter mCustomAdapter;
public setCustomAdapter(CustomAdapter adapter) {
// this method allows to setup the adapter at startup
mCustomAdapter = adapter;
}
protected DataSetObserver mMyViewObserver = new MyObserver();
private class MyObserver extends DataSetObserver {
#Override
public void onChanged() {
mUpdateHandler.sendEmptyMessage(0);
}
}
private Handler mUpdateHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
updateMyView();
}
};
private void updateMyView() {
if (mView == null) {
return;
}
mView.setMainTextViewText(mCustomAdapter.getText());
}
}
And here you have it. Each time you call notifyChanged(), your observer gets called. In return, he invokes the handler that update the view.
Here you have it, leak free, thread safe custom adapter for any kind of view.
Related
I have ViewModel
class MyViewModel extends BaseViewModel{
public void foo(){
// some code or return some boolean
}
}
View Class
class MyView extends View{
private MyViewModel myviewmodel;
public void bindTo(MyViewModel viewModel) {
this.viewModel = viewModel;
context = viewModel.getContext();
validateView();
requestLayout();
}
private validateView(){
//some code
}
}
this bind view method bind with adapter
I want to get call back in Myview class when ever i will validateView will call please suggest me how get call back from Viewmodel method to View in android.
it is best practice to use live data for communicating from viewmodel to your view.
class MyViewModel {
private MutableLiveData<Boolean> state = new MutableLiveData<Boolean>;
public LiveData<Boolean> getState() {
return state;
}
public void foo() {
//bool = value returned of your work
state.setValue(bool);
} }
class Myview extends View {
public void onCreate() {
viewmodel.getState().observe(this, observer); // 'this' is life cycle owner
}
final Observer<Boolean> observer = new Observer<Boolean>() {
#Override
public void onChanged(#Nullable final Boolean state) {
// do your work with returned value
}
}; }
for more details refer to this
Correct Me if i wrong
first you need to make interface class
public interface ViewModelCallback {
void returnCallBack(Boolean mBoolean);
}
then your View class implements that interface class & Override that method
class MyView extends View implements ViewModelCallback
#Override
public void returnCallBack(Boolean mBoolean) {
//here you will retrieve callback
// Do Something
}
Next you just pass a value from your view model
class MyViewModel {
private ViewModelCallback myViewCallBack;
public void foo() {
Boolean yourReturnValue = false;
myViewCallBack.returnCallBack(yourReturnValue);
}
}
I simplified my code for you to get better understanding it.
I have MyAdapter and MyActivity working both perfectly.
class MyAdapter extends RecyclerView... {
...
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
...
holder.mImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
((Listener) context).onSelected(object);
}
});
}
...
}
class MyActivity implements MyAdapter.Listener {
...
#Override
public void onSelected(Object object) {
// do things with object here
}
...
}
I want to make my another activity MyAnotherActivity implement MyAdapter.Listener.
When I run my app, and click on my object, an overriden onSelected() just ignored.
public class MyAnotherActivity implements MyAdapter.Listener {
...
#Override
public void onSelected(Object object) {
Log.e("MyAnotherActivity", "This text doesn't shows");
}
...
}
The used Listener implementation depends on the context parameter passed to MyAdapter constructor. Because on this line ((Listener) context).onSelected(object); you are casting the context field of the MyAdapter class to Listener implementation.
So when you are calling the constructor of the MyAdapter, you need to pass the context of MyAnotherActivity.
public class MyAnotherActivity implements MyAdapter.Listener {
#Override
protected void onCreate(Bundle savedInstanceState) {
...
MyAdapter adapter = new MyAdapter(this /*Context of MyAnotherActivity*/, ...);
...
}
}
If you want to implement any methods inside of any class (not just activity) , you can use EventBus, a lightweight library for passing messages and events around.
It's very easy to implement and the code samples will help you out along the way
http://greenrobot.org/eventbus/
I would like to try and use the MVP pattern for a simple Android application I am writing. This is my first time using the MVP pattern, as I'm still learning, so please be gentle ;)
I have a fragment that I would like to use in conjunction with 4 different presenters. The problem I have is, how can I pass a different presenter to each instance?
I would like to pass the presenter to a constructor, however when Android recreates the fragment, it will call the default constructor. Does this mean it will no longer hold a reference to the presenter?
If so, how else can I pass in the presenter?
I've included some psuedo code of what I'd like to do below. Please note I've just typed this straight into the browser so there may be some silly mistakes, but hopefully you get a rough idea of what I mean.
My 2 interfaces:
public interface IClickableListPresenter {
ListAdapter createListAdapter();
void onListItemClick(int position);
}
public interface ITabbable {
String getTitle();
Fragment getFragment();
}
2 example presenters:
public class ArtistPresenter implements IClickableListPresenter {
public ListAdapter createListAdapter(){
// Create a ListAdapter containing a list of artists
}
public void onListItemClick(int position){
// Handle the click event
}
}
public class TitlePresenter implements IClickableListPresenter {
public ListAdapter createListAdapter(){
// Create a ListAdapter containing a list of song titles
}
public void onListItemClick(int position){
// Handle the click event in a completely different way
// to the ArtistPresenter
}
}
My fragment:
public class ClickableListFragment extends ListFragment
implements ITabbable {
private IClickableListPresenter presenter;
private String title;
// What can I do instead of this constructor?
public ClickableListFragment(
String title, IClickableListPresenter presenter){
this.title = title;
this.presenter = presenter;
}
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setListAdapter(presenter.createListAdapter());
}
#Override
public void onListItemClick(ListView l, View v, int position, long id){
presenter.onListItemClick(position);
}
public Fragment getFragment(){
return this;
}
public String getTitle(){
return title;
}
}
And finally, the class that instantiates the fragments:
public class TabsPagerAdapter extends FragmentPagerAdapter{
private ITabbable tabs[] = {
new ClickableListFragment("Artist", new ArtistPresenter()),
new ClickableListFragment("Title", new TitlePresenter()),
//...
};
//...
}
You could do something like this
public class TabsPagerAdapter extends FragmentPagerAdapter{
private ITabbable tabs[] = {
ClickableListFragment.newInstance(ClickableListFragment.Type.ARTIST),
ClickableListFragment.newInstance(ClickableListFragment.Type.TITLE),
//...
};
}
In ClickableListFragment
public enum Type {
ARTIST,
TITLE
}
public static MyFragment newInstance(final Type fragmentType) {
final MyFragment fragment = new MyFragment();
Bundle args = new Bundle();
args.putSerializable(TYPE, fragmentType);
fragment.setArguments(args);
return fragment;
}
and in onCreate
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Type tempType = (Type) getArguments().getSerializable(TYPE);
if(tempType == Type.ARTIST){
variable = new ArtistPresenter();
.... what you want with that variable
.
.
.
}
I am currently building a camera app and have a problem:
I have got a fragment that should contain a camerapreview. Now this camerapreview is a custom implementation as it extends SurfaceView.
Now my actual question is, how should my custom camerapreview talk to the fragment? For instance, I would like to be able to let my fragment know, that a touch event occured.
What approach should i use?
There are multiple ways to do this. If you don't need this to be reusable, then a solution with a higher amount of coupling is ok. In which case the fragment tells the view about itself and the view can just call a method:
class CustomCameraPreview extends SurfaceView
{
FragmentA fragment;
// Call from onCreateView() in the framgnet
public void setFragment(FragmentA fragment)
{
this.fragment = fragment;
}
private void someMethod() {
if ( fragment != null)
fragment.callback();
}
}
public class FragmentA extends Fragment {
public void callback() {
// called from the view
}
}
If you need this to be more generic and reusable, then create a interface that contains the kind of callbacks the view would need to call and have the fragment implement that interface. This is basically the Observer pattern: http://en.wikipedia.org/wiki/Observer_pattern
interface CameraPreviewListener {
public void callback1() ;
public void callback2() ;
}
class CameraPreview extends SurfaceView
{
CameraPreviewListener listener;
// Call from onCreateView() in the framgnet
public void setFragment(CameraPreviewListener listener)
{
this.listener = listener;
}
private void someMethod1() {
if ( listener != null)
listener.callback1();
}
private void someMethod2() {
if ( listener != null)
listener.callback2();
}
}
public class FragmentA extends Fragment implements CameraPreviewListener{
public void callback1() {
// called from the view
}
public void callback2() {
// called from the view
}
}
Fragment object got field CameraPreview so it should be easy to inform your fragment class back if you make your CameraPreview inner class.
If you dont want to make CustomCameraPreview class inner, make your fragment implement MyOnClickListener and then pass to your cameraPreview object this listener. And if some event occurs inform listener with suitable method
class CustomCameraPreview extends SurfaceView
{
MyOnClickListener listener;
// some stuff
public void setListneer(MyOnClickListener listener)
{
this.listener = listener;
}
//// if someone touch something
if(listener!=null)
{
listener.somethingWasTouched();
}
}
interface MyOnClickListener
{
public void somethingWasTouched();
}
class MyFragment extends Fragment implements MyOnClickListener
{
#Override
public void somethingWasTouched()
{
//info that something was touched / profit
}
}
I have an activity which creates an object instance of my class:
file MyActivity.java:
public class MyActivity extends Activity {
TextView myView = (TextView)findViewById(R.id.myView);
...
Points myPoints new Points();
...
}
--------------------------------------------------------------
file Points.java:
private class Points {
...
HOW TO USE myView HERE ???
...
}
--------------------------------------------------------------
How do I use the UI objects in my class (which does not extend an
Activity)? Should I pass some context to my Points class? How do I do, exactly?
see you post, i've edited it , to fix the problem
hope it helps :=)
here is the Edit :
file MyActivity.java:
public class MyActivity extends Activity {
TextView myView ;
protected void onCreate(android.os.Bundle savedInstanceState) {
myView = (TextView)findViewById(R.id.myView);
Points myPoints = new Points(this);
myPoints.displayMsg("Hello World !!!");
}
}
--------------------------------------------------------------
file Points.java:
private class Points {
protected MyActivity context;
//add a constructor with the Context of your activity
public Points(MyActivity _context){
context = _context;
}
public void displayMsg( final String msg){
context.runOnUiThread(new Runnable() {
#Override
public void run() {
context.myView.setText(msg);
}
});
}
}
Your Points can't be a private class without being an inner class. So your code doesn't even compile...
Pass the view as parameter to the constructor of your Points class:
public class MyActivity extends Activity {
TextView myView = (TextView)findViewById(R.id.myView);
Points myPoints new Points(myView);
private class Points {
public Points(TextView view) {
// todo
}
}
}
You should do everything and pass back the value to the activity to handle UI instead of doing any UI related stuff in the point stuff.
You can pass the main Activity's context (using Points(getApplicationContext());) to the class as a constructor parameter. You could also pass the specific UI elements you want to manipulate.
A better way to do it, however, may be to have Points not know about the Activity. Have your Activity call Points methods and take the necessary actions based on the method output.
You could just pass the view to your class.
Points myPoints = new Points(myView);
private class Points
{
private TextView mTextView;
Points(TextView textView)
{
this.mTextView = textView;
}
}
i was in same trouble..
i found the simple way..
make a static variable and function ...
call from other class..
TestActivity.java
public class TestActivity extends Activity {
static EditText edit_text1;
public void onCreate(Bundle savedInstanceState)
{
.....
edit_text1 = (EditText) findViewById(R.id.edit_text1);
.....
}
public static void setMSG(String str)
{
edit_text1.setText(str);
}
}
Test2.java
TestActivity.setMSG("this is text");
Could work using an interface
file MyActivity.java:
public class MyActivity extends Activity implements Points.MyListener {
TextView myView;
... onCreate(...){
myView = (TextView)findViewById(R.id.myView);
Points myPoints = new Points();
//pass in MyActivity's instance of the listener
myPoints.addListener(this);
}
#Override
public void updateTextView(String message){
myView.setMessage(message);
}
}
file Points.java:
public class Points {
public Points(){
}
public interface MyListener{
void updateTextView(String message);
}
MyListener myListener;
public void addListener(MyListener listener){
myListener = listener;
}
public void updatePoints(){
//do some operations in calculatePoints()
String points = calculatePoints();
//update views using MyActivity's implementation of updateTextView()
myListener.updateTextView(points);
}
}
Doing it this way, events can be fired / messages sent, for lack of better terms, from the external class to update the Activity UI. This might be overkill if all sb need is to call a method in the Points class that returns something