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
}
}
Related
Im trying to listen or pass data from an BotomSheetDialogFragment into Fragment to change something on the Fragment (Just like a picker).
I've tried with getTargetFragment to instantiate the listener but getting a compiler error Found: 'MyFragment', required: 'android.support.v4.app.Fragment' less..
Any ideas or i'm takin the wrong approach?
public class MyBottomSheetDialogFragment extends BottomSheetDialogFragment implements View.OnClickListener {
ReportType reportType;
public interface OnChooseReasonListener {
void onChooseReason(ReportType reportType);
}
OnChooseReasonListener listener;
#Override
public void setupDialog(Dialog dialog, int style) {
super.setupDialog(dialog, style);
View contentView = View.inflate(getContext(), R.layout.picker_bottom_sheet_, null);
dialog.setContentView(contentView);
CoordinatorLayout.LayoutParams layoutParams =
(CoordinatorLayout.LayoutParams) ((View) contentView.getParent()).getLayoutParams();
CoordinatorLayout.Behavior behavior = layoutParams.getBehavior();
//get null here!!!:
listener = (OnChooseReasonListener) getParentFragment();// or with getTargetFragment();
}
#Override
public void onClick(View view) {
switch (view.getId()){
case R.id.cool_button:
this.reportType = ReportType.ME;
//trying to execute the lisstener on null
listener.onChooseReason(this.reportType);
dismiss();
break;
}
}}
And the fragment:
public class MyFragment extends Fragment
implements View.OnClickListener,
MyBottomSheetDialogFragment.OnChooseReasonListener {
//....code here
public void showPicker() {
//getting and compiler error Wrong 1st argument type.
// picker. setTargetFragment(MyFragment.this , 300);
picker.show(fm, picker.getTag());
}
#Override
public void onChooseReason(ReportType reportType) {
//not getting here
Log(TAG, "You choose something" + reportType.getValue());
}
}
Besides that it's not working, that code smells a little since you're coupling MyBottomSheetDialogFragment with the object that created it.
The correct approach would be to have a method void setOnChooseReasonListener(OnChooseReasonListener listener) on MyBottomSheetDialogFragment and call it when you create the instance.
myBottomSheetDialogFragment.setOnChooseReasonListener(this);
You can approach this by using the interface
First
Create an interface class
interface CustomInterfaceClass {
public void callbackMethod(String date);
}
Second,
Initialize the interface class in Activity or fragment
As I am using in the fragments class
//interface for callback
private CustomInterface callback;
Third, Make sure you have initialized the callback interface object within the onCreateView or OnCreate method.
//if you facing an error while initializing such as this keyword
not assigned to the callback method that means you didn't implement
the interface fragmentAclass.
callback=this;
Fourth,
Don't forget to implement the override method within the FragmentAClass
#Override
public void callbackMethod(String date) {
Toast.makeText(getContext(), "Yes"+date, Toast.LENGTH_SHORT).show();
}
Fifth,
Now move to BottomSheetDialogFragment or FragmentBclass
Add callback method constructor such as this
private CustomInterface callback;
public Disconnect_fragment( CustomInterface callback) {
this.callback=callback;
}
public Disconnect_fragment( ) {
}
Lastly Now you can pass the value by using this method and will receive in the FragmentAclass
callback.callbackMethod("your passing value");
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 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.
I am trying to create a communication between a custom View and a DialogFragment with an interface/callback.
Custom View:
public MyDraw extends View implements ColorPickerListener
{
public MyDraw(Context context)
{
super(context);
// ...
MyDialogFragment.setColorPickerListener(this);
}
#Override
public void onColorChanged(int color)
{
// ...
}
}
DialogFragment
public MyDialogFragment extends DialogFragment
{
public interface ColorPickerListener
{
public void onColorChanged(int color);
}
ColorPickerListener colorPickerListener;
public static void setColorPickerListener(ColorPickerListener listener)
{
colorPickerListener = listener;
}
// ....
private void colorSelected(int color)
{
colorPickerListener.onColorChanged(color);
}
}
This is working, but I'm not sure if this is Ok. I am afraid of memory leaks, because I am referencing a static method from View to the dialog fragment.
Is there any alternative solution, like getting the activity, the instance or casting to something?
You don't need to call the static setColorPickerListener method. You can find your DialogFragment instance using findFragmentByTag method and then simply call your setColorPickerListener (non-static method).
public void showPickerDialog() {
DialogFragment newFragment = new PickerFragment();
newFragment.show(this.getSupportFragmentManager(), "dialogfrag1");
getSupportFragmentManager().executePendingTransactions();
// getting the fragment
PickerFragment df1 = (PickerFragment) getSupportFragmentManager().findFragmentByTag("dialogfrag1");
if (df1 != null) {
df1.registerListener(this);
}
}
i've seen this thread : How to implement a listener about implement listeners.
its actually pretty simple but i don't get how exactly its done and how to implement in my own code.
i have this static variable variable: AppLoader.isInternetOn.
i want to build a listener which will listen to this variable changes and update a TextView.
should i do this: ?
build an interface:
public interface InternetStateListener {
public void onStateChange();
}
run it in my activity:
public class MyActivity extends Activity {
private InternetStateListener mListener;
setTheListener(this);
public void setTheListener(InternetStateListener listen) {
mListener = listen;
}
private void onStateChange() {
if (mListener != null) {
if (AppLoader.isInternetOn)
text.setText("on")
else
text.setText("off")
}
}
}
Your Activity does nothing special, just register itself (since the interface is implemented directly in the class) with the Other class that provides the listener.
public class MyActivity extends Activity implements InternetManager.Listener {
private TextView mText;
private InternetManager mInetMgr;
/* called just like onCreate at some point in time */
public void onStateChange(boolean state) {
if (state) {
mText.setText("on");
} else {
mText.setText("off");
}
}
public void onCreate() {
mInetMgr = new InternetManager();
mInetMgr.registerListener(this);
mInetMgr.doYourWork();
}
}
The other class has to do pretty much all the work. Besides that it has to handle the registration of listeners it has to call the onStateChange method once something happend.
public class InternetManager {
// all the listener stuff below
public interface Listener {
public void onStateChange(boolean state);
}
private Listener mListener = null;
public void registerListener (Listener listener) {
mListener = listener;
}
// -----------------------------
// the part that this class does
private boolean isInternetOn = false;
public void doYourWork() {
// do things here
// at some point
isInternetOn = true;
// now notify if someone is interested.
if (mListener != null)
mListener.onStateChange(isInternetOn);
}
}
The part that you're missing it the class that actually notifies the listener. So you would need a class (most likely a service) that runs and pings the state of the network. Then when it detects a change it should call onStateChange() in any registered listeners. Then you would call setTheListener on that service, not on your activity.
Here's a link that thoroughly describes this design pattern: http://en.wikipedia.org/wiki/Observer_pattern