martes, 22 de septiembre de 2015

Android: Animation with Animators and Animation Set

Heart animating
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
     params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
     params.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
     
     Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.heart_icon);
     
     final ImageView image = new ImageView(getActivity());
     image.setLayoutParams(params);
     image.setImageBitmap(bitmap);
     
     heartsContainer.addView(image);
     params.leftMargin = bitmap.getWidth()/2;
     params.topMargin = bitmap.getHeight();
        
        ObjectAnimator scaleX = ObjectAnimator.ofFloat(image, "scaleX", 0.5f, 1.0f);
        ObjectAnimator scaleY = ObjectAnimator.ofFloat(image, "scaleY", 0.5f, 1.0f);
        scaleX.setDuration(ANIMATION_APPEAR_TIME);
        scaleY.setDuration(ANIMATION_APPEAR_TIME);
        
        ObjectAnimator translateY = ObjectAnimator.ofFloat(image, "translationY", -(getResources().getDimension(R.dimen.hearts_container_height) - bitmap.getHeight()));
        translateY.setDuration(ANIMATION_TIME);
        
        ObjectAnimator horizontal = ObjectAnimator.ofFloat(image, "translationX", getRandomValueAllowNegative(bitmap.getWidth() * 1.5f, true));
        horizontal.setDuration(ANIMATION_TIME - 500);
        horizontal.setStartDelay(ANIMATION_APPEAR_TIME + 300);
        
        ObjectAnimator rotation = ObjectAnimator.ofFloat(image , "rotation", getRandomValueAllowNegative(60f, true));
        rotation.setDuration((long)getRandomValueAllowNegative(ANIMATION_TIME, false));
        
        ObjectAnimator alpha = ObjectAnimator.ofFloat(image , "alpha", 0f);
        alpha.setDuration(ANIMATION_TIME/2);
        alpha.setStartDelay(ANIMATION_TIME/2 + ANIMATION_APPEAR_TIME);
        
        AnimatorSet animationSet = new AnimatorSet();
        animationSet.play(scaleX).with(scaleY).with(translateY).with(horizontal).with(rotation).with(alpha);

martes, 15 de septiembre de 2015

iOS: Add remove view controllers to navigation controller

Add view controller from storyboard
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
[[self navigationController] pushViewController:[storyboard instantiateViewControllerWithIdentifier:@"MainViewController"] animated:YES]; 

Remove view controller
b
Replace rootview controller
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UIViewController* viewController = [storyboard instantiateViewControllerWithIdentifier:@"new_root_view_controller"];
    
[[self navigationController] setViewControllers:@[viewController] animated:YES];

martes, 8 de septiembre de 2015

Android: Google maps, too many markers, avoid addding marker latency

MapFragment
public class MapFragment extends BaseFragment {

    private GoogleMap mMap;
    private CameraPosition previousCameraPosition;
    private HashMap<Marker, Facility> markerToData;
    private Facility selectedFacility;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        markerToData = new HashMap<>();
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.activity_maps, null);
    }

    @Override
    public void onResume() {
        super.onResume();
        setUpMapIfNeeded();
    }

    private void setUpMapIfNeeded() {

        if (mMap == null) {
            mMap = ((SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map)).getMap();
            mMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {

                @Override
                public void onCameraChange(CameraPosition newCameraPosition) {
                    if (differentCameraPosition(newCameraPosition)) {
                        previousCameraPosition = newCameraPosition;
                        showMarkers();
                    }
                }
            });
            mMap.setInfoWindowAdapter(new FacilityInfoWindow());
            mMap.setOnMyLocationChangeListener(new GoogleMap.OnMyLocationChangeListener() {
                @Override
                public void onMyLocationChange(Location location) {

                    CameraUpdate center = CameraUpdateFactory.newLatLng(new LatLng(location.getLatitude(), location.getLongitude()));
                    CameraUpdate zoom = CameraUpdateFactory.zoomTo(11);
                    mMap.moveCamera(center);
                    mMap.animateCamera(zoom);
                }
            });
            mMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
                @Override
                public boolean onMarkerClick(Marker marker) {
                    selectedFacility = markerToData.get(marker);

                    return false;
                }
            });
        }
    }

    private boolean differentCameraPosition(CameraPosition newCameraPosition) {

        if (previousCameraPosition == null) {
            return true;
        }

        boolean differentZoom =  Float.floatToIntBits(previousCameraPosition.zoom) != Float.floatToIntBits(newCameraPosition.zoom);
        boolean differentLatitude =  Double.doubleToLongBits(previousCameraPosition.target.latitude) != Double.doubleToLongBits(newCameraPosition.target.latitude);
        boolean differentLongitude =  Double.doubleToLongBits(previousCameraPosition.target.longitude) != Double.doubleToLongBits(newCameraPosition.target.longitude);

        return differentZoom || differentLatitude || differentLongitude;
    }

    private void showMarkers() {

        if (mMap != null) {
            LatLngBounds bounds = mMap.getProjection().getVisibleRegion().latLngBounds;

            LatLng northeast = bounds.northeast;
            LatLng southwest = bounds.southwest;

            mMap.clear();

            List<Facility> facilities = Cache.getFacilitiesInBounds(northeast, southwest);

            BitmapDescriptor defaultMarker = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE);

            for (Facility facility : facilities) {

                Marker marker = mMap.addMarker(new MarkerOptions()
                        .position(new LatLng(facility.getLatitude(), facility.getLongitude()))
                        .draggable(false)
                        .visible(true)
                        .icon(defaultMarker)
                        .title(facility.getName()));

                markerToData.put(marker, facility);

                if (selectedFacility!= null && selectedFacility.getFacilityId() == facility.getFacilityId()) {
                    marker.showInfoWindow();
                    selectedFacility = null;
                }
            }
        }
    }

    private class FacilityInfoWindow implements GoogleMap.InfoWindowAdapter {

        @Override
        public View getInfoWindow(Marker marker) {

            Facility facility = markerToData.get(marker);

            View view = LayoutInflater.from(getActivity()).inflate(R.layout.facility_info_window, null);
            ((TextView)view.findViewById(R.id.title)).setText(facility.getName());
            ((TextView)view.findViewById(R.id.subtitle)).setText(facility.getAddress());

            return view;
        }

        @Override
        public View getInfoContents(Marker marker) {
            return null;
        }
    }
}
Important:
"differentCameraPosition" is necesary since "onCameraChange" will be called every time you click on a marker even if you are already centered on it, resulting in the info window to disappear a second later after clicking on the marker. Also use "selectedFacility" to avoid the info window from disappearing when centering the map around the marker.

lunes, 7 de septiembre de 2015

Android: EditTexts and TextViews various kind of intput and styles (email, password, username, etc fields)

Password (Default font, clears text when clicked)
Layout
<EditText
        android:id="@+id/login_password"
        ...
        android:inputType="textPassword"
        ... />
Code
passwordEditText = (EditText) this.findViewById(R.id.login_password);
passwordEditText.setTypeface(Typeface.DEFAULT);
passwordEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
                @Override
                public void onFocusChange(View v, boolean hasFocus) {
                    if (hasFocus) { // Clears the EditText when clicked
                        passwordEditText.setText("");
                    }
                }
            });
Email (No autocorrect)
Layout
<EditText
        ...
        android:inputType="textEmailAddress|text"
        .../>
Username (Capitalize each word)
Layout
<EditText
        ...
        android:inputType="textCapWords"
        .../>
a
b