plattysoft / Leonids

A Particle System for standard Android UI: http://plattysoft.github.io/Leonids/
Apache License 2.0
2.28k stars 398 forks source link

Variable particle center point #81

Open JoelNieman opened 7 years ago

JoelNieman commented 7 years ago

Great library. Having an issue with using it inside a ViewHolder though. Basically I have a recyclerView with 3 sections, in the 3rd (Footer) I have a pin centered on a map, among other things. The placement of the pin is the center point for where I want the particles emitted from.

My issue is, if I scroll quickly to the bottom of the recycler view to see the map, the particles are emitted from the correct location. However, if I slowly scroll down the recycler view, they will appear in the top left corner of the map instead. Seems really weird that it would do that. Any idea why this might be happening?

Below is the code for the fragment. Testing on a Samsung S7.

public class PredictionDayFragment extends Fragment implements SMTSliderView.OnSliderValueChangeListener, SMTRecyclerViewAdapter.OnAdapterListener, SMTToolbar.OnToolbarListener {

private static final String STYLE_SATELLITE = "mapbox://styles/jecourte/cir3l554z0027bsm1xdjbuunb";

// Variables
private SMTSliderView mSliderView;
private PredictionFragment mPredictionFragment;
private SMTRecyclerView mRecyclerView;
private SMTRecyclerViewAdapter mAdapter;
private ForecastDay mForecastDay;
private SMTToolbar mToolbar;
private FooterViewHolder mFooter;
private RealmList<ForecastFactor> mFactors;
private ParticleSystem mParticles;
private Integer peakHour;
private MapboxMap map;

/**
 * Constructors
 */
public PredictionDayFragment() {
    super();
}

/**
 * Passes a reference of the parent fragment
 *
 * @param predictionFragment
 */
@SuppressLint("ValidFragment")
public PredictionDayFragment(PredictionFragment predictionFragment, ForecastDay forecastDay) {
    super();

    // Gets a reference to the parent fragment
    mPredictionFragment = predictionFragment;

    // Get forecast day
    mForecastDay = forecastDay;

    // Get slider ref
    mSliderView = mPredictionFragment.getSliderView();
}

@SuppressLint("ValidFragment")
public PredictionDayFragment(PredictionFragment predictionFragment, ForecastDay forecastDay, Integer peakHour) {
    super();

    // Gets a reference to the parent fragment
    mPredictionFragment = predictionFragment;

    // Get forecast day
    mForecastDay = forecastDay;
    this.peakHour = peakHour;
    // Get slider ref
    mSliderView = mPredictionFragment.getSliderView();
}

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

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);

    View view = inflater.inflate(R.layout.fragment_prediction_day, null);

    mToolbar = (SMTToolbar) view.findViewById(R.id.toolbar);
    mToolbar.setTintColor(R.color.white_smt);
    mToolbar.setLeftIcon(R.drawable.ic_back_inverse, this);
    mToolbar.setTitle(RealmController.getInstance().getForecastSpecies().getName() + " " + getResources().getString(R.string.prediction));

    // Get Recycler View
    mRecyclerView = (SMTRecyclerView) view.findViewById(R.id.recycler_view);

    // Data set
    mFactors = mForecastDay.getHours().get(mSliderView.getValue()).getRating().getFactors();

    // Adapter
    mAdapter = new SMTRecyclerViewAdapter(mFactors, this);
    mAdapter.setListItemView(R.layout.prediction_day_list_item);
    mAdapter.setHeaderView(R.layout.prediction_day_header_item);
    mAdapter.setFooterView(R.layout.prediction_day_footer_item);

    // Link RecyclerView and Adapter
    mRecyclerView.setAdapter(mAdapter);

    // Prevent animations
    mRecyclerView.setItemAnimator(null);

    return view;
}

@Override
public void onStart() {
    super.onStart();

    // Ensure view has a value change listener
    mSliderView.setOnValueChangeListener(this);

    if (peakHour != null) {
        mSliderView.setValue(peakHour);
    }

    // Show toolbar if hidden
    mPredictionFragment.mToolbar.setVisibility(View.GONE);
}

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

@Override
public void onSliderValueChange(int time) {
    if (mAdapter != null) {

        // Reload set in range
        mAdapter.reloadDataSetRange(0, mFactors.size() + 1);

        // Check for footer ref
        if (mFooter != null) {

            // Get the factor for index
            ForecastHour hour = mForecastDay.getHours().get(mSliderView.getValue());

            // Set values
            mFooter.mWindDirectionTextView.setText(hour.getWind().getCompass());
            mFooter.mWindDirectionImageView.setRotation(hour.getWind().getDegree() - 180);
            mFooter.mWindSpeedTextView.setText(hour.getWind().getMph() + "mph");
            Picasso.with(getContext()).load(UrlUpdater.getUrlForDensity(getContext(),
                    hour.getCondition().getIconUrl())).into(mFooter.mConditionImageView);
            mFooter.mTempTextView.setText(hour.getTemp().getF() + "°");

            // Set the particle effect
            setWindParticles(hour.getWind());
        }
    }
}

/**
 * List Interfaces
 *
 * @param dataItemIndex
 */
@Override
public void onLastListItemShown(int dataItemIndex) {
}

@Override
public void onListItemAtIndex(SMTViewHolder viewHolder, int index) {

    // Get the factor for index
    final ForecastHour hour = mForecastDay.getHours().get(mSliderView.getValue());

    // Set view for type
    switch (viewHolder.getItemViewType()) {
        case SMTRecyclerViewAdapter.HEADER:

            // Get the factor for index
            double ratingAtIndex = hour.getRating().getPercent();

            // Get holder
            final HeaderViewHolder header = new HeaderViewHolder(viewHolder.itemView);

            // Set Values
            header.mRatingView.setRating(mPredictionFragment.mSpeciesBitmap, ratingAtIndex);
            header.mGraphView.setValues(mForecastDay.getHours(), mSliderView.getValue(), mPredictionFragment.mSpeciesBitmap);
            header.mTitle.setText("@ " + RealmController.getInstance().getForecastLocation().getName()
                    + "\n" + hour.getTimeAsHourampm() + " - " + mForecastDay.getDateAsPredictionDayViewTitle());

            break;

        case SMTRecyclerViewAdapter.LIST_ITEM:

            // Get the factor for index
            ForecastFactor factorForIndex = mForecastDay.getHours().get(mSliderView.getValue()).getRating().getFactors().get(index);

            // Get holder
            final ListItemViewHolder listItem = new ListItemViewHolder(viewHolder.itemView);

            // Set Values
            listItem.mFactorName.setText(factorForIndex.getName());
            listItem.mFactorSubText.setText(factorForIndex.getSubtext());
            listItem.mRatingView.setRating(mPredictionFragment.mSpeciesBitmap, factorForIndex.getPercent());

            break;

        case SMTRecyclerViewAdapter.FOOTER:

            // Get holder
            if (mFooter == null) {
                mFooter = new FooterViewHolder(viewHolder.itemView);

                // Set Values
                mFooter.mWindDirectionTextView.setText(hour.getWind().getCompass());
                mFooter.mWindDirectionImageView.setRotation(hour.getWind().getDegree() - 180);
                mFooter.mWindSpeedTextView.setText(hour.getWind().getMph() + "mph");
                Picasso.with(getContext()).load(UrlUpdater.getUrlForDensity(getContext(),
                        hour.getCondition().getIconUrl())).into(mFooter.mConditionImageView);
                mFooter.mTempTextView.setText(hour.getTemp().getF() + "°");

                // Set Map
                mFooter.mMapView.onCreate(null);
                mFooter.mMapView.setStyleUrl(STYLE_SATELLITE);
                mFooter.mMapView.setOnTouchListener(new View.OnTouchListener() {
                    @Override
                    public boolean onTouch(View v, MotionEvent event) {
                        return true;
                    }
                });

                // Load Map
                mFooter.mMapView.getMapAsync(new OnMapReadyCallback() {
                    @Override
                    public void onMapReady(MapboxMap mapboxMap) {
                        mFooter.bindFooterView(mapboxMap);
                        mFooter.mMapView.onResume();
                    }
                });
            }
            break;
    }
}

/**
 * Set the particles to the correct wind
 * @param wind
 */
private void setWindParticles(ForecastWind wind) {

    // Prevent wind from being absolute 0
    int trueWind = 1;
    if (wind.getMph() > 0) {
        trueWind = wind.getMph();
    }

    // Set values
    float maxSpeed = (float) (trueWind * 0.010);
    float minSpeed = (float) (trueWind * 0.005);
    int windDirection = wind.getDegree() + 90;
    int division = 180 / trueWind;
    int minAngle = windDirection - division;
    int maxAngle = windDirection + division;

    // Set particles styles
    try {
        mParticles.setScaleRange(0.3f, 0.5f);
        mParticles.setSpeedModuleAndAngleRange(minSpeed, maxSpeed, minAngle, maxAngle);
        mParticles.setRotationSpeedRange(1, 360);
        mParticles.setFadeOut(600, new AccelerateInterpolator());
    } catch (Exception e) {
        Log.e("PredictionDayFragment", e.getLocalizedMessage());
    }
}

/**
 * Toolbar Actions
 */
@Override
public void onLeftToolbarItemClick() {
    mPredictionFragment.getInnerNavController().pop();
}

@Override
public void onRightToolbarItemClick() {
    // Nothing
}

/**
 * Header View Holder
 */
private static class HeaderViewHolder extends SMTViewHolder {

    // Views
    SMTRatingView mRatingView;
    AppFontTextView mTitle;
    SMTGraphContainer mGraphView;

    // Init
    HeaderViewHolder(View view) {
        super(view);
        mRatingView = (SMTRatingView) view.findViewById(R.id.rating_view);
        mTitle = (AppFontTextView) view.findViewById(R.id.title);
        mGraphView = (SMTGraphContainer) view.findViewById(R.id.graph_view);
    }
}

/**
 * List Item View Holder
 */
private static class ListItemViewHolder extends SMTViewHolder {

    // Views
    AppFontTextView mFactorName;
    AppFontTextView mFactorSubText;
    SMTRatingView mRatingView;

    // Init
    ListItemViewHolder(View view) {
        super(view);
        mFactorName = (AppFontTextView) view.findViewById(R.id.name);
        mFactorSubText = (AppFontTextView) view.findViewById(subtext);
        mRatingView = (SMTRatingView) view.findViewById(rating_view);
    }
}

/**
 * Footer View Holder
 */
private class FooterViewHolder extends SMTViewHolder {

    // Views
    AppFontTextView mWindDirectionTextView;
    ImageView mWindDirectionImageView;
    AppFontTextView mWindSpeedTextView;
    ImageView mConditionImageView;
    AppFontTextView mTempTextView;
    MapView mMapView;
    ImageView mPin;
    CardView mMapParentView;
    FrameLayout mFilterView;
    View mParticleCenter;

    // Init
    FooterViewHolder(View view) {
        super(view);
        mWindDirectionTextView = (AppFontTextView) view.findViewById(R.id.wind_direction);
        mWindDirectionImageView = (ImageView) view.findViewById(R.id.wind_direction_image_view);
        mWindSpeedTextView = (AppFontTextView) view.findViewById(wind_speed);
        mConditionImageView = (ImageView) view.findViewById(R.id.weather_condition_image_view);
        mTempTextView = (AppFontTextView) view.findViewById(R.id.temp);
        mMapView = (MapView) view.findViewById(R.id.map_view);
        mPin = (ImageView) view.findViewById(R.id.marker_pin);
        mMapParentView = (CardView) view.findViewById(R.id.map_parent);
        mFilterView = (FrameLayout) view.findViewById(R.id.filter_view);
        mParticleCenter = view.findViewById(R.id.particle_center);
    }

    public void bindFooterView(MapboxMap map) {
        LocationModel mLocation = RealmController.getInstance().getForecastLocation();

        // Get values
        double lat = mLocation.getLatitude();
        double lng = mLocation.getLongitude();
        LatLng coordinate = new LatLng(lat, lng);

        // Set camera position
        map.moveCamera(CameraUpdateFactory.newCameraPosition(
                new CameraPosition.Builder()
                        .target(coordinate)
                        .zoom(16.2)
                        .tilt(0)
                        .build()));

        // Set particle effect
        mParticles = new ParticleSystem(mFooter.mFilterView, 60, ContextCompat.getDrawable(getContext(), R.drawable.icon_leaflet), 1800);
        setWindParticles(mForecastDay.getHours().get(mSliderView.getValue()).getWind());
        mParticles.emit(mFooter.mParticleCenter, 30);
    }

}

public void setPeakHour(int hour) {
    this.peakHour = hour;
}

}

fllaryora commented 2 years ago

I had a resembling error. If I use a chart view (from a library), the particles will appear in the top left corner of the chart instead of the center. Any idea why this might be happening? No, but I solved it using the container of my view as anchor.