Implementing Master/Detail with:
- adaptive
master_detail
with a handset breakpoint - a controller factory.
- Interface based contract between fragment/activity/controller whie keeping them decoupled
-
- main activity with implements the intererface for
Implementing Master/Detail with:
master_detail
with a handset breakpoint<?xml version="1.0" encoding="utf-8"?> | |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:paddingBottom="@dimen/activity_vertical_margin" | |
android:paddingLeft="@dimen/activity_horizontal_margin" | |
android:paddingRight="@dimen/activity_horizontal_margin" | |
android:paddingTop="@dimen/activity_vertical_margin" | |
tools:context="com.example.avi.advancedfragmentexample.activities.MainActivity"> | |
<fragment | |
android:id="@+id/fragment" | |
android:name="com.example.avi.advancedfragmentexample.fragments.Fragment1" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_weight="1" /> | |
</LinearLayout> |
package com.example.avi.advancedfragmentexample.activities; | |
import android.support.v4.app.FragmentActivity; | |
import android.os.Bundle; | |
import com.example.avi.advancedfragmentexample.controllers.ControllersFactory; | |
import com.example.avi.advancedfragmentexample.R; | |
import com.example.avi.advancedfragmentexample.fragments.Fragment1; | |
import com.google.android.gms.maps.model.LatLng; | |
public class MainActivity extends FragmentActivity implements Fragment1.IUserActions { | |
private Fragment1.IUserActions userActionsController; | |
@Override protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.main_act); | |
// Getting a tablet / mobile controller (the factory hides the decision making). | |
userActionsController = ControllersFactory.createUserInteractionsController(this); | |
} | |
// Delegate event handling to the controller | |
@Override public void onFocusOnLocation(LatLng newLocation){ | |
userActionsController.onFocusOnLocation(newLocation); | |
} | |
} |
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
tools:context="com.example.avi.advancedfragmentexample.fragments.MasterFragment"> | |
<Button | |
android:id="@+id/button1" | |
android:text="focus on map" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_margin="20dp" | |
/> | |
</FrameLayout> |
package com.example.avi.advancedfragmentexample.fragments; | |
import android.app.Activity; | |
import android.content.Context; | |
import android.content.Intent; | |
import android.os.Bundle; | |
import android.support.v4.app.Fragment; | |
import android.util.Log; | |
import android.view.LayoutInflater; | |
import android.view.View; | |
import android.view.ViewGroup; | |
import android.widget.Button; | |
import com.example.avi.advancedfragmentexample.R; | |
import com.google.android.gms.maps.SupportMapFragment; | |
import com.google.android.gms.maps.model.LatLng; | |
public class MasterFragment extends Fragment { | |
private IUserActions parentActivity; | |
@Override | |
public View onCreateView(LayoutInflater inflater, ViewGroup container, | |
Bundle savedInstanceState) { | |
View fragmentView = inflater.inflate(R.layout.master_frag, container, false); | |
Button button = (Button) fragmentView.findViewById(R.id.button1); | |
Log.d("Avi", "Showing GIT before and after, comparison between 2 commits"); | |
// The fragment delegates the click event, to the activity | |
// Because in this specific excercise, we've decided that the button's behavior context related | |
// context related = the activity decides (the behavior can change in different activities) | |
button.setOnClickListener(new View.OnClickListener() { | |
@Override | |
public void onClick(View v) { | |
LatLng newLocation = new LatLng(32.109333, 34.855499); | |
parentActivity.onFocusOnLocation(newLocation); | |
} | |
}); | |
return fragmentView; | |
} | |
@Override | |
// The context is in fact the activity which hosts the fragment | |
// This function is being called after the activity is being created | |
public void onAttach(Context context) { | |
super.onAttach(context); | |
if (context instanceof IUserActions) { | |
// If the activity which holds the current fragment, obeys to the rules in the | |
// "contract", defined in the interface ("IUserActions"), then we save a | |
// reference to the external activity, in order to call it, each time the button | |
// had been pressed | |
this.parentActivity = (IUserActions) context; | |
} else { | |
throw new RuntimeException(context.toString() | |
+ " must implement IUserActions"); | |
} | |
} | |
@Override | |
public void onDetach() { | |
super.onDetach(); | |
parentActivity = null; | |
} | |
public interface IUserActions { | |
public void onFocusOnLocation(LatLng newLocation); | |
} | |
} |
<?xml version="1.0" encoding="utf-8"?> | |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:app="http://schemas.android.com/apk/res-auto" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:id="@+id/layout" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
tools:context="com.example.avi.advancedfragmentexample.fragments.MapFragment"> | |
<fragment | |
android:id="@+id/map" | |
android:name="com.google.android.gms.maps.SupportMapFragment" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
/> | |
</LinearLayout> |
package com.example.avi.advancedfragmentexample.fragments; | |
import android.app.Activity; | |
import android.content.Context; | |
import android.content.Intent; | |
import android.os.Bundle; | |
import android.support.v4.app.Fragment; | |
import android.view.LayoutInflater; | |
import android.view.View; | |
import android.view.ViewGroup; | |
import com.example.avi.advancedfragmentexample.R; | |
import com.google.android.gms.maps.CameraUpdateFactory; | |
import com.google.android.gms.maps.GoogleMap; | |
import com.google.android.gms.maps.OnMapReadyCallback; | |
import com.google.android.gms.maps.SupportMapFragment; | |
import com.google.android.gms.maps.model.LatLng; | |
import com.google.android.gms.maps.model.MarkerOptions; | |
public class MapFragment extends Fragment implements OnMapReadyCallback, IUserActionsOnMap { | |
private GoogleMap mMap; | |
private LatLng currentLocation; | |
@Override | |
public void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
// This section will need refactoring on the long run | |
LatLng defaultLocation = new LatLng(-33.865143, 151.209900); | |
this.currentLocation = defaultLocation; | |
// Exreacrting the intent from the wrapping activity | |
Intent intent = ((Activity) getContext()).getIntent(); | |
// Initializing the map's location based on a command sent by the previous activity | |
// This code segment is ONLY relevant to mobile mode, and not tablet mode | |
String commandName = intent.getStringExtra("commandName"); | |
if (commandName != null && commandName.equals("focusOnLocation")) { | |
double latitude = intent.getDoubleExtra("latitude", 0); | |
double longitude = intent.getDoubleExtra("longitude", 0); | |
this.currentLocation = new LatLng(latitude, longitude); | |
} | |
} | |
@Override | |
public View onCreateView(LayoutInflater inflater, ViewGroup container, | |
Bundle savedInstanceState) { | |
View view = inflater.inflate(R.layout.fragment_fragment2, container, false); | |
SupportMapFragment mapFragment = (SupportMapFragment) getChildFragmentManager() | |
.findFragmentById(R.id.map); | |
mapFragment.getMapAsync(this); | |
return view; | |
} | |
@Override | |
public void onMapReady(GoogleMap googleMap) { | |
mMap = googleMap; | |
mMap.moveCamera(CameraUpdateFactory.newLatLng(this.currentLocation)); | |
} | |
@Override | |
public void onFocusOnLocation(LatLng newLocation) { | |
this.currentLocation = newLocation; | |
if (mMap != null) { | |
mMap.animateCamera(CameraUpdateFactory.newLatLng(newLocation)); | |
mMap.addMarker(new MarkerOptions().position(newLocation)); | |
} | |
} | |
} |
<?xml version="1.0" encoding="utf-8"?> | |
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:paddingBottom="@dimen/activity_vertical_margin" | |
android:paddingLeft="@dimen/activity_horizontal_margin" | |
android:paddingRight="@dimen/activity_horizontal_margin" | |
android:paddingTop="@dimen/activity_vertical_margin" | |
tools:context="com.example.avi.advancedfragmentexample.activities.DetailActivity"> | |
<fragment | |
android:id="@+id/map" | |
android:name="com.example.avi.advancedfragmentexample.fragments.MapFragment" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_alignParentEnd="true" | |
android:layout_alignParentRight="true" | |
android:layout_alignTop="@+id/fragment2" | |
android:layout_marginEnd="29dp" | |
android:layout_marginRight="29dp" | |
tools:layout="@layout/fragment_fragment2" /> | |
</RelativeLayout> |
package com.example.avi.advancedfragmentexample.activities; | |
import android.support.v4.app.Fragment; | |
import android.support.v4.app.FragmentActivity; | |
import android.support.v7.app.AppCompatActivity; | |
import android.os.Bundle; | |
import android.widget.TextView; | |
import com.example.avi.advancedfragmentexample.R; | |
import com.example.avi.advancedfragmentexample.fragments.IUserActionsOnMap; | |
import com.google.android.gms.maps.SupportMapFragment; | |
import com.google.android.gms.maps.model.LatLng; | |
public class DetailActivity extends FragmentActivity { | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_second); | |
double latitude = getIntent().getDoubleExtra("latitude", 0); | |
double longitude = getIntent().getDoubleExtra("longitude", 0); | |
// SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.fragment2); | |
// The fragment will read the new arguments and will initialize itself based on them | |
// savedInstanceState.put.setArguments(bundleArguments); | |
} | |
} |
package com.example.avi.advancedfragmentexample.fragments; | |
import com.google.android.gms.maps.model.LatLng; | |
public interface IUserActionsOnMap { | |
public void onFocusOnLocation(LatLng location); | |
} |
package com.example.avi.advancedfragmentexample.controllers; | |
import android.support.v4.app.FragmentActivity; | |
import com.example.avi.advancedfragmentexample.fragments.Fragment1; | |
import com.example.avi.advancedfragmentexample.fragments.IUserActionsOnMap; | |
import com.example.avi.advancedfragmentexample.R; | |
public class ControllersFactory { | |
public static Fragment1.IUserActions createUserInteractionsController(FragmentActivity activity) { | |
// Getting a reference to the maps fragment, ONLY FOUND IN THE TABLET MODE | |
IUserActionsOnMap mapFragment = (IUserActionsOnMap) activity.getSupportFragmentManager().findFragmentById(R.id.map); | |
// If the fragment exists --> Tablet mode | |
if (mapFragment != null) { | |
return new TabletController(mapFragment); | |
} | |
else | |
{ | |
// Fragment is null --> Mobile mode | |
return new MobileController(activity); | |
} | |
} | |
} |
package com.example.avi.advancedfragmentexample.controllers; | |
import com.example.avi.advancedfragmentexample.fragments.Fragment1; | |
import com.example.avi.advancedfragmentexample.fragments.IUserActionsOnMap; | |
import com.google.android.gms.maps.model.LatLng; | |
class TabletController implements Fragment1.IUserActions { | |
private IUserActionsOnMap mapsFragment; | |
public TabletController(IUserActionsOnMap mapsFragment){ | |
this.mapsFragment = mapsFragment; | |
} | |
public void onFocusOnLocation(LatLng newLocation){ | |
mapsFragment.onFocusOnLocation(newLocation); | |
} | |
} |
package com.example.avi.advancedfragmentexample.controllers; | |
import android.app.Activity; | |
import android.content.Intent; | |
import android.os.Bundle; | |
import com.example.avi.advancedfragmentexample.fragments.Fragment1; | |
import com.example.avi.advancedfragmentexample.activities.SecondActivity; | |
import com.google.android.gms.maps.model.LatLng; | |
class MobileController implements Fragment1.IUserActions { | |
private Activity activity; | |
public MobileController(Activity activity){ | |
this.activity = activity; | |
} | |
@Override | |
public void onFocusOnLocation(LatLng newLocation) { | |
Intent intent = new Intent(activity, SecondActivity.class); | |
intent.putExtra("commandName", "focusOnLocation"); | |
intent.putExtra("latitude", newLocation.latitude); | |
intent.putExtra("longitude", newLocation.longitude); | |
Bundle bundle = new Bundle(); | |
bundle.putDouble("latitude", newLocation.latitude); | |
bundle.putDouble("longitude", newLocation.longitude); | |
intent.putExtras(bundle); | |
activity.startActivity(intent); | |
} | |
} |