Android: Cara menggunakan efek Paralaks di Aplikasi mobile

efek Paralaks di Aplikasi mobile
Efek pengguliran paralaks membuat gelombang sebagai tren desain web maupun mobile app yang baru dan menarik. paralaks telah ditetapkan sebagai aset desain mendasar yang akan tetap ada hingga saat ini. Paralaks didasarkan pada ilusi optik. Karena mata manusia menganggap benda-benda yang dekat dengan kita lebih besar daripada benda-benda yang lebih jauh, kita melihat benda-benda yang jauh seolah-olah mereka bergerak lebih lambat. Paralaks adalah efek di mana konten latar belakang atau gambar yang terlihat di dalam, bergerak dengan kecepatan yang berbeda dari konten latar depan saat menggulir. contohnya anda bida lihat demo dibawah ini:


Untuk menggunakannya, Anda hanya perlu mendefinisikannya dalam activity_parallax.xml seperti ini.


<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ParallaxActivity" />

Buatlah dua buah file fragment dan untuk fragment_satu.xml terapkan baris kode dibawah ini:


<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:pew="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ParallaxDemo$FragmentPlaceholder">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <FrameLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

            <com.happycodx.apps.ImageViewParallax
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:layout_gravity="center"
                android:layout_margin="10dp"
                android:scaleType="centerCrop"
                android:src="@drawable/bali1"
                pew:reverse="reverseX"/>

            <com.happycodx.apps.TextViewParallax
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_margin="10dp"
                android:gravity="bottom|center_horizontal"
                android:text="@string/bali_0"
                android:textColor="@android:color/white"
                android:textSize="30sp"
                pew:block_parallax_x="true"
                pew:parallax_x="160dp"
                pew:parallax_y="160dp"
                pew:reverse="reverseX"/>
        </FrameLayout>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:text="@string/bali_1" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal"
            android:weightSum="2">

            <FrameLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_gravity="center"
                android:layout_margin="10dp"
                android:layout_weight="1">

                <com.happycodx.apps.ImageViewParallax
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_gravity="center"
                    android:layout_margin="10dp"
                    android:scaleType="centerCrop"
                    android:src="@drawable/bali2" />

                <com.happycodx.apps.TextViewParallax
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_margin="10dp"
                    android:gravity="bottom|center_horizontal"
                    android:text="@string/balix"
                    android:textColor="@android:color/white"
                    pew:block_parallax_x="true"
                    pew:parallax_x="160dp"
                    pew:parallax_y="160dp"
                    pew:reverse="reverseY" />

            </FrameLayout>

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_margin="10dp"
                android:layout_weight="1"
                android:text="@string/bali_2" />


        </LinearLayout>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:text="@string/bali_3" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal"
            android:weightSum="2">

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_margin="10dp"
                android:layout_weight="1"
                android:text="@string/bali_4" />


            <com.happycodx.apps.ImageViewParallax
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_margin="10dp"
                android:layout_weight="1"
                android:background="@android:color/darker_gray"
                android:scaleType="centerCrop"
                android:src="@drawable/bali3"
                pew:reverse="reverseY" />

        </LinearLayout>

    </LinearLayout>

</ScrollView>

Untuk file fragment_dua.xml terapkan baris kode seperti di bawah ini:


<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:pew="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ParallaxDemo$FragmentPlaceholder">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <FrameLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

            <com.happycodx.apps.ImageViewParallax
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:layout_gravity="center"
                android:layout_margin="10dp"
                android:scaleType="centerCrop"
                android:src="@drawable/diy1"
                pew:reverse="reverseX" />

            <com.happycodx.apps.TextViewParallax
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_margin="10dp"
                android:gravity="bottom|center_horizontal"
                android:text="@string/diy_0"
                android:textColor="@android:color/white"
                android:textSize="30sp"
                pew:block_parallax_x="true"
                pew:parallax_x="160dp"
                pew:parallax_y="160dp"
                pew:reverse="reverseX" />
        </FrameLayout>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:text="@string/diy_1" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <FrameLayout
                android:layout_width="match_parent"
                android:layout_height="140dp"
                android:layout_gravity="center"
                android:layout_margin="10dp"
                android:layout_weight="1">

                <com.happycodx.apps.ImageViewParallax
                    android:layout_width="match_parent"
                    android:layout_height="140dp"
                    android:layout_gravity="center"
                    android:layout_margin="10dp"
                    android:scaleType="centerCrop"
                    android:src="@drawable/diy2"
                    pew:reverse="reverseY" />


                <com.happycodx.apps.TextViewParallax
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="bottom|center_horizontal"
                    android:text="@string/diyx"
                    android:textColor="@android:color/white"
                    pew:block_parallax_x="true"
                    pew:parallax_x="160dp"
                    pew:parallax_y="160dp" />

            </FrameLayout>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="10dp"
                android:text="@string/diy_2" />

        </LinearLayout>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:text="@string/diy_3" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal"
            android:weightSum="2">

            <com.happycodx.apps.ImageViewParallax
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_gravity="center"
                android:layout_margin="10dp"
                android:layout_weight="1"
                android:background="@android:color/darker_gray"
                android:scaleType="centerCrop"
                android:src="@drawable/diy3" />

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_margin="10dp"
                android:layout_weight="1"
                android:text="@string/diy_4" />

        </LinearLayout>

    </LinearLayout>

</ScrollView>

Ini file about_activity.xml.


<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/about_background"
    android:background="#55000000"
    android:layout_width="match_parent"
	android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="50dp"
        android:background="@android:color/white"
        android:orientation="vertical"
        android:layout_gravity="center">

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/happycodx"
            android:layout_gravity="center_horizontal"
            android:layout_margin="5dp"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="26sp"
            android:layout_margin="10dp"
            android:text="ParallaxEverywhere"
            android:gravity="center"/>

        <TextView
            android:id="@+id/about_info"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:text=""/>

    </LinearLayout>

</FrameLayout>

Buat juga file contoh_parallax.xml didalam folder /app/src/main/res/menu/


<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".ParallaxActivity" >
    <item android:id="@+id/action_settings"
        android:title="@string/action_settings"
        android:orderInCategory="100"
        android:showAsAction="never" />
</menu>

Ini untuk file attrs.xml.


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="PEWAttrs">
        <attr name="reverse">
            <enum name="none" value="1" />
            <enum name="reverseX" value="2" />
            <enum name="reverseY" value="3" />
            <enum name="reverseBoth" value="4" />
        </attr>

        <attr name="parallax_x" format="dimension"/>
        <attr name="parallax_y" format="dimension"/>

        <attr name="update_onDraw" format="boolean"/>

        <attr name="block_parallax_x" format="boolean"/>
        <attr name="block_parallax_y" format="boolean"/>

        <attr name="interpolation">
            <enum name="linear" value="0" />
            <enum name="accelerate_decelerate" value="1" />
            <enum name="accelerate" value="2" />
            <enum name="anticipate" value="3" />
            <enum name="anticipate_overshoot" value="4" />
            <enum name="bounce" value="5" />
            <enum name="decelerate" value="6" />
            <enum name="overshoot" value="7" />
        </attr>

    </declare-styleable>
</resources>

Dan ini untuk strings.xml.


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Happycodx</string>
	<string name="title_section1">Denpasar Bali</string>
    <string name="title_section2">Yogyakarta</string>
    <string name="action_settings">About us</string>
    <string name="bali_1">"Kabupaten/kota yang mempunyai luas wilayah terbesar di Provinsi Bali adalah Kabupaten Buleleng dengan luas wilayah sebesar 1.364,73 km2, diikuti oleh Kabupaten Tabanan dengan luas wilayah sebesar 1.013,88 km2, dan kemudian oleh Kabupaten Jembrana dengan luas wilayah sebesar 841,80 km2. Ibu kota Provinsi Bali adalah Kota Denpasar. Berikut adalah daftar 9 kabupaten/kota di Provinsi Bali yang diurutkan secara alfabetis, lengkap dengan kode wilayah menurut Peraturan Kementerian Dalam Negeri Republik Indonesia"</string>
    <string name="bali_2">"Berikut adalah daftar 9 kabupaten/kota di Provinsi Bali yang diurutkan secara alfabetis, lengkap dengan kode wilayah menurut Peraturan Kementerian Dalam Negeri Republik Indonesia, nama ibu kota kabupaten dan luas wilayah dari masing-masing kabupaten/kota tersebut."</string>
    <string name="bali_3">"Daftar kabupaten/kota di provinsi lainnya:Aceh, Banten, Bengkulu, DI Yogyakarta, DKI Jakarta, Gorontalo, Jambi, Jawa Barat, Jawa Tengah, Jawa Timur, Kalimantan Barat, Kalimantan Selatan, Kalimantan Tengah, Kalimantan Timur, Kalimantan Utara, Kepulauan Bangka Belitung, Kepulauan Riau, Lampung,"</string>
    <string name="bali_4">"Data kode wilayah, nama kabupaten/kota, dan luas wilayah berdasarkan Peraturan Menteri Dalam Negeri Republik Indonesia Nomor 72 Tahun 2019 tentang Perubahan atas Peraturan Menteri Dalam Negeri Republik Indonesia Nomor 137 Tahun 2017 tentang Kode dan Data Wilayah Administrasi Pemerintahan."</string>
    <string name="bali_0">"Denpasar Bali"</string>
    <string name="diy_0">"Yogyakarta"</string>
    <string name="balix">"Denpasar Bali"</string>
    <string name="diy_1">"Popularitas Yogyakarta seakan tidak pernah pudar memancarkan pesonanya baik dalam bidang pariwisata, pendidikan ataupun kebudayaan. Kota Yogyakarta ini memang cukup punya nama besar dan bahkan bisa disejajarkan dengan popularitas Pulau Bali."</string>
    <string name="diy_2">"Banyak yang menjadikan Yogyakarta sebagai tujuan wisata baik bersama keluarga ataupun perjalanan bersama teman dan sahabat. Di bawah ini akan dikupas tuntas bagaimana informasi umum seperti sejarah, letak geografis, struktur pemerintahan daerah dan terutama sisi pariwisata Jogjakarta terkini."</string>
    <string name="diy_3">"Yogyakarta merupakan sebuah daerah istimewa dalam Negara Kesatuan Republik Indonesia yang masih mempertahankan tata pemerintahan berbentuk kesultanan dalam pemerintahan daerahnya."</string>
    <string name="diy_4">"Wilayah ini memiliki tata kelola pemerintahan dengan sistem kesultanan. Namun setelah RI merdeka, kedua pemerintahan ini bergabung dalam wilayah kesatuan RI dengan Undang-undang Istimewa yang mengatur tata pemerintahan daerahnya."</string>
    <string name="diyx">Yogyakarta</string>
    <string name="about_text_2"><![CDATA[This is a demo app of a library.<br/>Developed by:<br/><a href=\'https://www.happycodx.eu.org\'>Happycodx</a><br/>You can see more info at:<br/><a href=\'https://www.happycodx.eu.org\'>https://www.happycodx.eu.org/Android-Parallax.html</a>]]></string>
</resources>

Bagaimana cara kerjanya?

Setiap tampilan paralaks harus berada di dalam tampilan dengan peristiwa gulir. seperti, scrollView, listView, gridView. Efek paralaks pada tampilan akan terkait dengan posisinya di layar perangkat. Efek paralaks di ImageView dihitung dengan gambar kiri dalam mode Skala centerCrop, centerInside atau center. Anda tidak dapat membuat lebih banyak efek paralaks. Efek paralaks dalam tampilan gambar tidak memerlukan ukuran parameter paralaks.

Saya buat file MainActivity sebagai ParallaxActivity.java


package com.happycodx.apps;

import android.app.ActionBar;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import java.util.Locale;
import android.support.v4.view.ViewPager;
import android.support.v13.app.FragmentPagerAdapter;

/**
 * Created by happycodx on 02/03/2023.
 */
public class ParallaxActivity extends Activity implements ActionBar.TabListener {

    SectionsPagerAdapter mSectionsPagerAdapter;
    ViewPager mViewPager;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_parallax);
        final ActionBar actionBar = getActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        mSectionsPagerAdapter = new SectionsPagerAdapter(getFragmentManager());
        mViewPager = (ViewPager) findViewById(R.id.pager);
        mViewPager.setAdapter(mSectionsPagerAdapter);
        mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                actionBar.setSelectedNavigationItem(position);
            }
        });

        for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
            actionBar.addTab(
                    actionBar.newTab()
                            .setText(mSectionsPagerAdapter.getPageTitle(i))
                            .setTabListener(this));
        }
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.contoh_parallax, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            startActivity(AboutActivity.createIntent(this));
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
        mViewPager.setCurrentItem(tab.getPosition());
    }

    @Override
    public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
    }

    @Override
    public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
    }

    public class SectionsPagerAdapter extends FragmentPagerAdapter {

        public SectionsPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            return FragmentPlaceholder.newInstance(position);
        }

        @Override
        public int getCount() {
            return 2;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            Locale l = Locale.getDefault();
            switch (position) {
                case 0:
                    return getString(R.string.title_section1).toUpperCase(l);
                case 1:
                    return getString(R.string.title_section2).toUpperCase(l);
            }
            return null;
        }
    }

    public static class FragmentPlaceholder extends Fragment {
        private static final String ARG_SECTION_NUMBER = "section_number";
        public static FragmentPlaceholder newInstance(int sectionNumber) {
            FragmentPlaceholder fragment = new FragmentPlaceholder();
            Bundle args = new Bundle();
            args.putInt(ARG_SECTION_NUMBER, sectionNumber);
            fragment.setArguments(args);
            return fragment;
        }

        public FragmentPlaceholder() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            int anInt = getArguments().getInt(ARG_SECTION_NUMBER);
            int resource;
            switch (anInt) {
                case 0:
                default:
                    resource = R.layout.fragment_satu;
                    break;
                case 1:
                    resource = R.layout.fragment_dua;
                    break;
            }
            View rootView = inflater.inflate(resource, container, false);
            return rootView;
        }
    }

}

#. AboutActivity.java


package com.happycodx.apps;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.view.View;
import android.widget.TextView;

/**
 * Created by happycodx on 02/03/2023.
 */

public class AboutActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.about_activity);

        findViewById(R.id.about_background).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });

        TextView viewById = (TextView) findViewById(R.id.about_info);
        viewById.setMovementMethod(LinkMovementMethod.getInstance());
        viewById.setText(Html.fromHtml(getString(R.string.about_text_2)));
    }

    public static Intent createIntent(Context context) {
        return new Intent(context, AboutActivity.class);
    }
}

ImageViewParallax.java


package com.happycodx.apps;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Point;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import java.lang.Override;

/**
 * Created by happycodx on 02/03/2023.
 */
public class ImageViewParallax extends ImageView {

    public boolean reverseX = false;
    public boolean reverseY = false;
    public boolean updateOnDraw = false;
    public boolean blockParallaxX = false;
    public boolean blockParallaxY = false;

    private int screenWidth;
    private int screenHeight;
    private float scrollSpaceX = 0;
    private float scrollSpaceY = 0;
    private float heightImageView;
    private float widthImageView;

    private Interpolator interpolator = new LinearInterpolator();

    private ViewTreeObserver.OnScrollChangedListener mOnScrollChangedListener = null;
    private ViewTreeObserver.OnGlobalLayoutListener  mOnGlobalLayoutListener = null;
    private ViewTreeObserver.OnDrawListener onDrawListener = null;

    public ImageViewParallax(Context context) {
        super(context);
        checkScale();
    }

    public ImageViewParallax(Context context, AttributeSet attrs) {
        super(context, attrs);
        if (!isInEditMode()) {
            checkAttributes(attrs);
        }
        checkScale();
    }

    public ImageViewParallax(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        if (!isInEditMode()) {
            checkAttributes(attrs);
        }
        checkScale();
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        mOnScrollChangedListener = new ViewTreeObserver.OnScrollChangedListener() {
            @Override
            public void onScrollChanged() {
                applyParallax();
            }
        };

        mOnGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                heightImageView = (float) getHeight();
                widthImageView = (float) getWidth();

                applyParallax();
            }
        };

        ViewTreeObserver viewTreeObserver = getViewTreeObserver();
        viewTreeObserver.addOnScrollChangedListener(mOnScrollChangedListener);
        viewTreeObserver.addOnGlobalLayoutListener(mOnGlobalLayoutListener);

        if (updateOnDraw
                && android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            onDrawListener = new ViewTreeObserver.OnDrawListener() {
                @Override
                public void onDraw() {
                    applyParallax();
                }
            };
            viewTreeObserver.addOnDrawListener(onDrawListener);
        }

        parallaxAnimation();
    }

    @Override
    protected void onDetachedFromWindow() {
        ViewTreeObserver viewTreeObserver = getViewTreeObserver();
        viewTreeObserver.removeOnScrollChangedListener(mOnScrollChangedListener);
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            viewTreeObserver.removeOnGlobalLayoutListener(mOnGlobalLayoutListener);
        } else {
            viewTreeObserver.removeGlobalOnLayoutListener(mOnGlobalLayoutListener);
        }
        if (updateOnDraw
            && android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                viewTreeObserver.removeOnDrawListener(onDrawListener);
        }
        super.onDetachedFromWindow();
    }

    private boolean checkScale() {
        switch (getScaleType()) {
            case CENTER:
            case CENTER_CROP:
            case CENTER_INSIDE:
                return true;
            case FIT_CENTER:
                Log.d("ParallaxEverywhere", "Scale type firCenter unsupported");
                break;
            case FIT_END:
                Log.d("ParallaxEverywhere", "Scale type fitEnd unsupported");
                break;
            case FIT_START:
                Log.d("ParallaxEverywhere", "Scale type fitStart unsupported");
                break;
            case FIT_XY:
                Log.d("ParallaxEverywhere", "Scale type fitXY unsupported");
                break;
            case MATRIX:
                Log.d("ParallaxEverywhere", "Scale type matrix unsupported");
                break;
        }
        return false;
    }

    private void checkAttributes(AttributeSet attrs) {
        TypedArray arr = getContext().obtainStyledAttributes(attrs, R.styleable.PEWAttrs);
        int reverse = arr.getInt(R.styleable.PEWAttrs_reverse, 1);

        updateOnDraw = arr.getBoolean(R.styleable.PEWAttrs_update_onDraw, false);

        blockParallaxX = arr.getBoolean(R.styleable.PEWAttrs_block_parallax_x, false);
        blockParallaxY = arr.getBoolean(R.styleable.PEWAttrs_block_parallax_y, false);

        reverseX = false;
        reverseY = false;
        switch (reverse) {
            case KonstantAttr.REVERSE_NONE:
                break;
            case KonstantAttr.REVERSE_X:
                reverseX = true;
                break;
            case KonstantAttr.REVERSE_Y:
                reverseY = true;
                break;
            case KonstantAttr.REVERSE_BOTH:
                reverseX = true;
                reverseY = true;
                break;
        }

        checkScale();

        int interpolationId = arr.getInt(R.styleable.PEWAttrs_interpolation, 0);

        interpolator = SelectorInterpolator.interpolatorId(interpolationId);

        arr.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        if (getDrawable() != null) {

            int dheight = getDrawable().getIntrinsicHeight();
            int dwidth = getDrawable().getIntrinsicWidth();
            int vheight = getMeasuredHeight();
            int vwidth = getMeasuredWidth();

            float scale;

            float dnewHeight = 0;
            float dnewWidth = 0;

            switch (getScaleType()) {
                case CENTER_CROP:
                case CENTER:
                case CENTER_INSIDE:
                    if (dwidth * vheight > vwidth * dheight) {
                        scale = (float) vheight / (float) dheight;
                        dnewWidth = dwidth * scale;
                        dnewHeight = vheight;
                    } else {
                        scale = (float) vwidth / (float) dwidth;
                        dnewWidth = vwidth;
                        dnewHeight = dheight * scale;
                    }
                    break;
                case FIT_CENTER:
                case FIT_END:
                case FIT_START:
                case FIT_XY:
                case MATRIX:
                    break;
            }

            scrollSpaceY = (dnewHeight > vheight) ? (dnewHeight - vheight) : 0;
            scrollSpaceX = (dnewWidth > vwidth) ? (dnewWidth - vwidth) : 0;
        }
        applyParallax();
    }

    private void parallaxAnimation() {
        initSizeScreen();

        applyParallax();

    }

    private void initSizeScreen() {
        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
            Point size = new Point();
            display.getSize(size);
            screenHeight = size.y;
            screenWidth = size.x;
        } else {
            screenHeight = display.getHeight();
            screenWidth = display.getWidth();
        }
    }

    private void applyParallax() {
        int[] location = new int[2];
        getLocationOnScreen(location);

        if (scrollSpaceY != 0 && !blockParallaxY) {
            float locationY = (float) location[1];
            float locationUsableY = locationY + heightImageView / 2;
            float scrollDeltaY = locationUsableY / screenHeight;

            float interpolatedScrollDeltaY = interpolator.getInterpolation(scrollDeltaY);

            if (reverseY)
                setMyScrollY((int) (Math.min(Math.max((0.5f - interpolatedScrollDeltaY), -0.5f), 0.5f) * -scrollSpaceY));
            else
                setMyScrollY((int) (Math.min(Math.max((0.5f - interpolatedScrollDeltaY), -0.5f), 0.5f) * scrollSpaceY));
        }else{
            setMyScrollY(0);
        }

        if (scrollSpaceX != 0 && !blockParallaxX) {
            float locationX = (float) location[0];
            float locationUsableX = locationX + widthImageView / 2;
            float scrollDeltaX = locationUsableX / screenWidth;

            float interpolatedScrollDeltaX = interpolator.getInterpolation(scrollDeltaX);

            if (reverseX) {
                setMyScrollX((int) (Math.min(Math.max((0.5f - interpolatedScrollDeltaX), -0.5f), 0.5f) * -scrollSpaceX));
            } else {
                setMyScrollX((int) (Math.min(Math.max((0.5f - interpolatedScrollDeltaX), -0.5f), 0.5f) * scrollSpaceX));
            }
        }else{
            setMyScrollX(0);
        }
    }

    private void setMyScrollX(int value) {
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            setScrollX(value);
        } else {
            scrollTo(value, getScrollY());
        }
    }

    private void setMyScrollY(int value) {
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            setScrollY(value);
        } else {
            scrollTo(getScrollX(),value);
        }
    }

    public void setInterpolator(Interpolator interpol) {
        interpolator = interpol;
    }

    public boolean isReverseX() {
        return reverseX;
    }

    public void setReverseX(boolean reverseX) {
        this.reverseX = reverseX;
    }

    public boolean isReverseY() {
        return reverseY;
    }

    public void setReverseY(boolean reverseY) {
        this.reverseY = reverseY;
    }

    public boolean isBlockParallaxX() {
        return blockParallaxX;
    }

    public void setBlockParallaxX(boolean blockParallaxX) {
        this.blockParallaxX = blockParallaxX;
    }

    public boolean isBlockParallaxY() {
        return blockParallaxY;
    }

    public void setBlockParallaxY(boolean blockParallaxY) {
        this.blockParallaxY = blockParallaxY;
    }
}

TextViewParallax.java


package com.happycodx.apps;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Point;
import android.os.Build;
import android.util.AttributeSet;
import android.view.Display;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.widget.TextView;


/**
 * Created by happycodx on 02/03/2023.
 */
public class TextViewParallax extends TextView {

    public boolean reverseX = false;
    public boolean reverseY = false;
    public int scrollSpaceX = 0;
    public int scrollSpaceY = 0;
    public boolean updateOnDraw = false;
    public boolean blockParallaxX = false;
    public boolean blockParallaxY = false;

    private int screenHeight;
    private int screenWidth;
    private float heightView;
    private float widthView;
    private Interpolator interpolator = new LinearInterpolator();
    private ViewTreeObserver.OnScrollChangedListener mOnScrollChangedListener = null;
    private ViewTreeObserver.OnGlobalLayoutListener mOnGlobalLayoutListener = null;
    private ViewTreeObserver.OnDrawListener onDrawListener = null;

    public TextViewParallax(Context context) {
        super(context);
        if (!isInEditMode()) {
            parallaxAnimation();
        }
    }

    public TextViewParallax(Context context, AttributeSet attrs) {
        super(context, attrs);
        if (!isInEditMode()) {
            checkAttributes(attrs);
            parallaxAnimation();
        }
    }

    public TextViewParallax(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        if (!isInEditMode()) {
            checkAttributes(attrs);
            parallaxAnimation();
        }
    }

    private void checkAttributes(AttributeSet attrs) {
        TypedArray arr = getContext().obtainStyledAttributes(attrs, R.styleable.PEWAttrs);
        int reverse = arr.getInt(R.styleable.PEWAttrs_reverse, 1);

        updateOnDraw = arr.getBoolean(R.styleable.PEWAttrs_update_onDraw, false);

        blockParallaxX = arr.getBoolean(R.styleable.PEWAttrs_block_parallax_x, false);
        blockParallaxY = arr.getBoolean(R.styleable.PEWAttrs_block_parallax_y, false);

        reverseX = false;
        reverseY = false;
        switch (reverse) {
            case KonstantAttr.REVERSE_NONE:
                break;
            case KonstantAttr.REVERSE_X:
                reverseX = true;
                break;
            case KonstantAttr.REVERSE_Y:
                reverseY = true;
                break;
            case KonstantAttr.REVERSE_BOTH:
                reverseX = true;
                reverseY = true;
                break;
        }

        scrollSpaceX = arr.getDimensionPixelSize(R.styleable.PEWAttrs_parallax_x, 0);
        scrollSpaceY = arr.getDimensionPixelSize(R.styleable.PEWAttrs_parallax_y, 0);


        int interpolationId = arr.getInt(R.styleable.PEWAttrs_interpolation, 0);

        interpolator = SelectorInterpolator.interpolatorId(interpolationId);

        arr.recycle();
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        mOnScrollChangedListener = new ViewTreeObserver.OnScrollChangedListener() {
            @Override
            public void onScrollChanged() {
                applyParallax();
            }
        };

        mOnGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                heightView = (float) getHeight();
                widthView = (float) getWidth();

                applyParallax();
            }
        };

        ViewTreeObserver viewTreeObserver = getViewTreeObserver();
        viewTreeObserver.addOnScrollChangedListener(mOnScrollChangedListener);
        viewTreeObserver.addOnGlobalLayoutListener(mOnGlobalLayoutListener);

        if (updateOnDraw
                && android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            onDrawListener = new ViewTreeObserver.OnDrawListener() {
                @Override
                public void onDraw() {
                    applyParallax();
                }
            };
            viewTreeObserver.addOnDrawListener(onDrawListener);
        }

        parallaxAnimation();
    }

    @Override
    protected void onDetachedFromWindow() {
        ViewTreeObserver viewTreeObserver = getViewTreeObserver();
        viewTreeObserver.removeOnScrollChangedListener(mOnScrollChangedListener);
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            viewTreeObserver.removeOnGlobalLayoutListener(mOnGlobalLayoutListener);
        } else {
            viewTreeObserver.removeGlobalOnLayoutListener(mOnGlobalLayoutListener);
        }
        if (updateOnDraw
                && android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            viewTreeObserver.removeOnDrawListener(onDrawListener);
        }
        super.onDetachedFromWindow();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        applyParallax();
    }


    private void parallaxAnimation() {
        initSizeScreen();

        applyParallax();
    }


    private void initSizeScreen() {
        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
            Point size = new Point();
            display.getSize(size);
            screenHeight = size.y;
            screenWidth = size.x;
        } else {
            screenHeight = display.getHeight();
            screenWidth = display.getWidth();
        }
    }

    private void applyParallax() {
        int[] location = new int[2];
        getLocationOnScreen(location);

        if (scrollSpaceY != 0
                && !blockParallaxY) {
            float locationY = (float) location[1];
            float locationUsableY = locationY + heightView / 2;
            float scrollDeltaY = locationUsableY / screenHeight;

            float interpolatedScrollDeltaY = interpolator.getInterpolation(scrollDeltaY);

            if (reverseY)
                setMyScrollY((int) (Math.min(Math.max((0.5f - interpolatedScrollDeltaY), -0.5f), 0.5f) * -scrollSpaceY));
            else
                setMyScrollY((int) (Math.min(Math.max((0.5f - interpolatedScrollDeltaY), -0.5f), 0.5f) * scrollSpaceY));
        }

        if (scrollSpaceX != 0
                && !blockParallaxX) {
            float locationX = (float) location[0];
            float locationUsableX = locationX + widthView / 2;
            float scrollDeltaX = locationUsableX / screenWidth;

            float interpolatedScrollDeltaX = interpolator.getInterpolation(scrollDeltaX);

            if (reverseX) {
                setMyScrollX((int) (Math.min(Math.max((0.5f - interpolatedScrollDeltaX), -0.5f), 0.5f) * -scrollSpaceX));
            } else {
                setMyScrollX((int) (Math.min(Math.max((0.5f - interpolatedScrollDeltaX), -0.5f), 0.5f) * scrollSpaceX));
            }
        }
    }

    private void setMyScrollX(int value) {
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            setScrollX(value);
        } else {
            scrollTo(value, getScrollY());
        }
    }

    private void setMyScrollY(int value) {
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            setScrollY(value);
        } else {
            scrollTo(getScrollX(),value);
        }
    }

    public void setInterpolator(Interpolator interpol) {
        interpolator = interpol;
    }

    public boolean isReverseX() {
        return reverseX;
    }

    public void setReverseX(boolean reverseX) {
        this.reverseX = reverseX;
    }

    public boolean isReverseY() {
        return reverseY;
    }

    public void setReverseY(boolean reverseY) {
        this.reverseY = reverseY;
    }

    public int getScrollSpaceX() {
        return scrollSpaceX;
    }

    public void setScrollSpaceX(int scrollSpaceX) {
        this.scrollSpaceX = scrollSpaceX;
    }

    public int getScrollSpaceY() {
        return scrollSpaceY;
    }

    public void setScrollSpaceY(int scrollSpaceY) {
        this.scrollSpaceY = scrollSpaceY;
    }

    public boolean isBlockParallaxX() {
        return blockParallaxX;
    }

    public void setBlockParallaxX(boolean blockParallaxX) {
        this.blockParallaxX = blockParallaxX;
    }

    public boolean isBlockParallaxY() {
        return blockParallaxY;
    }

    public void setBlockParallaxY(boolean blockParallaxY) {
        this.blockParallaxY = blockParallaxY;
    }
}

KonstantAttr.java


package com.happycodx.apps;

/**
 * Created by happycodx on 02/03/2023.
 */
public class KonstantAttr {
    static final int REVERSE_NONE = 1;
    static final int REVERSE_X = 2;
    static final int REVERSE_Y = 3;
    static final int REVERSE_BOTH = 4;
}

SelectorInterpolator.java


package com.happycodx.apps;

import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnticipateInterpolator;
import android.view.animation.AnticipateOvershootInterpolator;
import android.view.animation.BounceInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.OvershootInterpolator;

/**
 * Created by happycodx on 02/03/2023.
 */
public class SelectorInterpolator {
    private static final int LINEAR = 0;
    private static final int ACCELERATE_DECELERATE = 1;
    private static final int ACCELERATE = 2;
    private static final int ANTICIPATE = 3;
    private static final int ANTICIPATE_OVERSHOOT = 4;
    private static final int BOUNCE = 5;
    private static final int DECELERATE = 6;
    private static final int OVERSHOOT = 7;

    public static Interpolator interpolatorId(int interpolationId) {
        switch (interpolationId) {
            case LINEAR:
            default:
                return new LinearInterpolator();
            case ACCELERATE_DECELERATE:
                return new AccelerateDecelerateInterpolator();
            case ACCELERATE:
                return new AccelerateInterpolator();
            case ANTICIPATE:
                return new AnticipateInterpolator();
            case ANTICIPATE_OVERSHOOT:
                return new AnticipateOvershootInterpolator();
            case BOUNCE:
                return new BounceInterpolator();
            case DECELERATE:
                return new DecelerateInterpolator();
            case OVERSHOOT:
                return new OvershootInterpolator();
            //TODO: this interpolations needs parameters
            //case CYCLE:
            //    return new CycleInterpolator();
            //case PATH:
            //    return new PathInterpolator();
        }
    }
}


Baca Juga
Posting Komentar (0)
Lebih baru Lebih lama