Skip to content

Instantly share code, notes, and snippets.

@luisburgos
Created March 14, 2017 02:32
Show Gist options
  • Save luisburgos/e298a5cb39a3de2c912eb9e84dd1b01e to your computer and use it in GitHub Desktop.
Save luisburgos/e298a5cb39a3de2c912eb9e84dd1b01e to your computer and use it in GitHub Desktop.
Collapsing toolbar with map fragment inside and view pager
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
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/information_coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsingToolbarLayout"
android:layout_width="match_parent"
android:layout_height="280dp"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginStart="48dp"
app:expandedTitleMarginEnd="64dp"
app:layout_scrollFlags="scroll|snap">
<FrameLayout
android:id="@+id/mapContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_collapseMode="parallax"/>
</android.support.design.widget.CollapsingToolbarLayout>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="56dp"
android:background="?colorPrimary"
android:layout_marginTop="-56dp"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:layout_scrollFlags="scroll|enterAlways|snap" />
<include
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="-112dp"
layout="@layout/activity_model_information_header" />
<android.support.design.widget.TabLayout
android:id="@+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:tabIndicatorHeight="4dp"
app:tabMode="fixed" />
</android.support.design.widget.AppBarLayout>
<DeactivatableViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
<?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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
app:layout_scrollFlags="scroll|enterAlways|snap">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="112dp"
android:padding="6dp"
android:gravity="center">
<TextView
android:id="@+id/circleView"
android:layout_width="100dp"
android:layout_height="100dp"
android:text="@string/name"
android:background="@drawable/background_circle"
android:textSize="18sp"
android:gravity="center"
android:textStyle="bold"
android:textColor="@color/white"/>
</LinearLayout>
<TextView
android:id="@+id/nameTextView"
android:layout_width="match_parent"
android:layout_height="28dp"
android:gravity="center"
android:text="@string/name"
android:textAppearance="@style/TextAppearance.AppCompat.Widget.ActionBar.Title" />
<TextView
android:id="@+id/descriptionTextView"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="28dp"
android:text="@string/unique_identifier"
android:textAppearance="@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle" />
</LinearLayout>
/**
* Helper class which provides methods to being, redirect, start and finish activities.
*/
public class ActivityHelper {
/**
* The {@code fragment} is added to the container view with id {@code frameId}. The operation is
* performed by the {@code fragmentManager}.
*
*/
public static void addFragmentToActivity(
@NonNull FragmentManager fragmentManager,
@NonNull Fragment fragment,
int frameId
) {
checkNotNull(fragmentManager);
checkNotNull(fragment);
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(frameId, fragment);
transaction.commit();
}
}
public class ActivityModelInformation extends InformationActivity {
private HeaderViewHolder headerInformationHolder;
private TabLayout tabLayout;
private DeactivatableViewPager viewPager;
private MapViewFragment mapFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_model_information);
super.baseSetup();
setupViewPager();
setupMapFragment();
}
@Override
protected void onResume() {
super.onResume();
//CALL PRESENTER TO FETCH DATA
}
@Override
protected void initViews() {
super.initViews();
viewPager = (DeactivatableViewPager) findViewById(R.id.viewpager);
tabLayout = (TabLayout) findViewById(R.id.sliding_tabs);
headerInformationHolder = new HeaderViewHolder(findViewById(android.R.id.content));
//INITIALIZE PRESENTER.
}
@Override
protected void setInformationData() {
Bundle bundle = getIntent().getExtras();
//SET CLASS VARIABLES from BUNDLE
updateUIHeader();
}
/**
* This method is NOT used by this class.
* @param isCollapsed state of toolbar.
*/
@Override
protected void onToolbarCollapsed(boolean isCollapsed) {
//IMPORTANT! Do nothing
}
/**
* Retrieves a custom activity title for display on toolbar.
* @return activity title.
*/
@Override
public String getActivityTitle() {
return "Custom Activity Title";
}
/**
* Updates the flowmeter header information.
*/
private void updateUIHeader(){
//UPDATE VALUES ON HEADER VIEW HOLDER
}
/**
* Setup the view pager that shows information about the parameters.
*/
private void setupViewPager() {
//INITIALIZE THE ADAPTER
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
public void onPageScrollStateChanged(int state) {}
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
public void onPageSelected(int position) {
boolean pagingEnable = true;
/*if(position == MyAdapter.PAGE_NEED_IT_TO_MATCH){
pagingEnable = false;
}*/
viewPager.setPagingEnabled(pagingEnable);
}
});
viewPager.setAdapter(sectionsPagerAdapter);
tabLayout.setupWithViewPager(viewPager);
}
/**
* This method setups a {@link MapViewFragment} to show the flowmeter location.
* This method also validates if Google Play Services are available on the current device.
* If there is no Google Play Services API available the map is not displayed on UI.
*/
private void setupFlowmeterMapFragment() {
final int status = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this);
if (status != ConnectionResult.SUCCESS) {
FrameLayout mapContainer = (FrameLayout) findViewById(R.id.mapContainer);
mapContainer.setVisibility(View.GONE);
} else {
mapFragment = (MapViewFragment) getSupportFragmentManager()
.findFragmentById(R.id.mapContainer);
if (mapFragment == null) {
mapFragment = MapViewFragment.newInstance(/* Parameters if you need */);
ActivityHelper.addFragmentToActivity(
getSupportFragmentManager(), mapFragment, R.id.mapContainer);
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/white" />
<size
android:height="100dp"
android:width="100dp"/>
<padding
android:bottom="15dp"
android:top="15dp"
android:left="15dp"
android:right="15dp"/>
</shape>
/**
* Allows to deactivate the paging on a {@link ViewPager} component.
*/
public class DeactivatableViewPager extends ViewPager {
private boolean isPagingEnabled = true;
public DeactivatableViewPager(Context context) {
super(context);
}
public DeactivatableViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return this.isPagingEnabled && super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return this.isPagingEnabled && super.onInterceptTouchEvent(event);
}
public void setPagingEnabled(boolean b) {
this.isPagingEnabled = b;
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/tools"
android:id="@+id/supportMapContainer"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:cameraZoom="13"
map:mapType="normal"
map:liteMode="true" />
<LinearLayout
android:id="@+id/mapButtons"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginRight="15dip"
android:layout_marginEnd="15dip"
android:layout_marginTop="15dip">
<ImageButton
android:id="@+id/location"
android:src="@drawable/ic_crosshair_gps"
style="@style/MapControlButton"/>
<ImageButton
android:id="@+id/zoom_in"
android:layout_marginLeft="5dip"
android:layout_marginStart="5dip"
android:src="@drawable/ic_plus"
style="@style/MapControlButton"/>
<ImageButton
android:id="@+id/zoom_out"
android:src="@drawable/ic_minus"
android:layout_marginLeft="5dip"
android:layout_marginStart="5dip"
style="@style/MapControlButton"/>
</LinearLayout>
</RelativeLayout>
/**
* Created by luisburgos on 8/18/16.
*/
public class HeaderViewHolder extends RecyclerView.ViewHolder {
public View cirlceView;
public TextView nameTextView;
public TextView descriptionTextView;
public HeaderViewHolder(View itemView) {
super(itemView);
cirlceView = itemView.findViewById(R.id.cirlceView);
nameTextView = (TextView) itemView.findViewById(R.id.nameTextView);
descriptionTextView = (TextView) itemView.findViewById(R.id.descriptionTextView);
}
}
/**
* Base UI View to show a basic information about a domain model.
* This class has basic components like {@link CoordinatorLayout} and
* {@link AppBarLayout}.
* Created by luisburgos on 8/31/16.
*/
public abstract class InformationActivity extends AppCompatActivity {
protected CoordinatorLayout coordinatorLayout;
protected AppBarLayout appBarLayout;
protected CollapsingToolbarLayout collapsingToolbarLayout;
protected Toolbar toolbar;
/**
* This method has sequential cohesion in order to achieve a
* correct configuration for the activity. This method NEEDS to be called from
* concrete implementations.
*
* This method depends on {@link InformationActivity#initViews()},
* {@link InformationActivity#setInformationData()}, {@link InformationActivity#setupToolbar()},
* {@link InformationActivity#setupAppBar()}
*/
protected void baseSetup(){
this.initViews();
this.setInformationData();
this.setupToolbar();
this.setupAppBar();
}
/**
* BINDS xml components to class variables.
*/
protected void initViews() {
coordinatorLayout = (CoordinatorLayout) findViewById(R.id.information_coordinator_layout);
collapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsingToolbarLayout);
toolbar = (Toolbar) findViewById(R.id.toolbar);
appBarLayout = (AppBarLayout) findViewById(R.id.appbar);
}
/**
* Displays home back button and enables back pressed behavior.
*/
protected void setupToolbar() {
setSupportActionBar(toolbar);
if(getSupportActionBar() != null){
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowTitleEnabled(false);
}
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBackPressed();
}
});
}
/**
* Sets a {@link AppBarLayout.OnOffsetChangedListener} to the AppBar and notifies when
* offset change and is less than 25.
*/
protected void setupAppBar() {
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
boolean isShow = false;
int scrollRange = -1;
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (scrollRange == -1) {
scrollRange = appBarLayout.getTotalScrollRange();
}
if (scrollRange + verticalOffset <= 25) {
onToolbarCollapsed(true);
isShow = true;
} else if(isShow) {
onToolbarCollapsed(false);
isShow = false;
}
}
});
}
/**
* Shows a {@link Snackbar} with the message to show.
* @param message text to show.
*/
protected void showMessage(String message) {
if(coordinatorLayout == null){
return;
}
Snackbar.make(coordinatorLayout, message, Snackbar.LENGTH_LONG).show();
}
/**
* This method is NOT used by this class.
* @param isCollapsed state of toolbar.
*/
protected abstract void onToolbarCollapsed(boolean isCollapsed);
/**
* Retrieves a custom activity title for display on toolbar.
* @return activity title.
*/
protected abstract String getActivityTitle();
/**
* This method is needed to retrieve information a set the current activity information.
* It could be necessary to implement a extra method to update the UI.
*/
protected abstract void setInformationData();
}
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
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.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
/**
*
*/
public class MapViewFragment extends Fragment implements OnMapReadyCallback {
private SupportMapFragment mapFragment;
private GoogleMap mMap = null;
private Marker mMarker;
private LatLng initialPoint;
public MapViewFragment(){
//REQUIRES EMPTY CONSTRUCTOR
}
public static MapViewFragment newInstance( /* Add parameters if you need */ ){
Bundle arguments = new Bundle();
/*arguments.putSerializable(KEY, value);*/
MapViewFragment fragment = new MapViewFragment();
fragment.setArguments(arguments);
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_model_location, container, false);
setupMapControlButtons(root);
return root;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//GET VALUES FROM ARGUMENTS.
/*valueVariable = (String) getArguments().getSerializable(KEY);*/
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
FragmentManager fm = getChildFragmentManager();
mapFragment = (SupportMapFragment) getActivity().getSupportFragmentManager().findFragmentById(R.id.map);
if (mapFragment == null) {
mapFragment = SupportMapFragment.newInstance();
fm.beginTransaction().replace(R.id.map, mapFragment).commit();
}
}
@Override
public void onResume() {
super.onResume();
mapFragment.getMapAsync(this);
}
@Override
public void onMapReady(GoogleMap googleMap) {
this.mMap = googleMap;
initialPoint = /* IMPORTANT!! FIND A WAY TO PARSE A LAT LNG POSITION*/
if(initialPoint != null){
setupMarker(initialPoint);
setupMarkerInfoAction();
}
}
private void setupMarkerInfoAction() {
mMap.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
@Override
public void onInfoWindowClick(Marker marker) {
displayOpenOnGoogleMapsDialog();
}
});
}
private void displayOpenOnGoogleMapsDialog() {
//IMPORTANT! Display a Dialog to Open on Google Maps.
}
private void zoomToCurrentLatLngPosition(LatLng initialPoint) {
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(initialPoint)
.zoom(9)
.build();
mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
}
private void setupMarker(@NonNull LatLng initialPoint) {
this.mMarker = this.mMap.addMarker(
new MarkerOptions()
.position(initialPoint)
.title(yourCustomTitleHere)
.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_custom_marker_of_your_choice))
);
mMarker.showInfoWindow();
zoomToCurrentLatLngPosition(initialPoint);
}
private void setupMapControlButtons(View root) {
final ImageButton locationButton = (ImageButton) root.findViewById(R.id.location);
final ImageButton zoomInButton = (ImageButton) root.findViewById(R.id.zoom_in);
final ImageButton zoomOutButton = (ImageButton) root.findViewById(R.id.zoom_out);
locationButton.setVisibility(View.VISIBLE);
zoomInButton.setVisibility(View.VISIBLE);
zoomOutButton.setVisibility(View.VISIBLE);
locationButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(initialPoint != null){
zoomToCurrentLatLngPosition(initialPoint);
}
}
});
zoomInButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mMap.animateCamera(CameraUpdateFactory.zoomIn());
}
});
zoomOutButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mMap.animateCamera(CameraUpdateFactory.zoomOut());
}
});
}
}
<resources>
<style name="MapControlButton">
<item name="android:layout_width">25dip</item>
<item name="android:layout_height">25dip</item>
<item name="android:visibility">gone</item>
<item name="android:background">@drawable/background_dodgerblue_rounded_corners</item>
</style>
</resources>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment