Support
Quality
Security
License
Reuse
kandi has reviewed RecyclerViewPager and discovered the below as its top functions. This is intended to give you an instant insight into RecyclerViewPager implemented functionality, and help decide if they suit your requirements.
Deprecated
how to import?
// Yes, I have switched to jitpack.io.
repositories {
...
maven { url "https://jitpack.io" }
...
}
dependencies {
...
compile 'com.github.lsjwzh.RecyclerViewPager:lib:v1.2.0@aar'
...
}
Basic Usage:
<com.lsjwzh.widget.recyclerviewpager.RecyclerViewPager
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="15dp"
android:paddingRight="15dp"
app:rvp_triggerOffset="0.1"
app:rvp_singlePageFling="true"
android:clipToPadding="false"/>
Support TabLayout:
dependencies {
...
compile 'com.github.lsjwzh.RecyclerViewPager:tablayoutsupport:v1.1.2@aar'
...
}
Infinite Loop ViewPager:
<com.lsjwzh.widget.recyclerviewpager.LoopRecyclerViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="20dp"
android:clipToPadding="false"
android:paddingTop="15dp"
android:paddingBottom="15dp"/>
Release Notes:
1.2.0 update support lib to 26.0.2
1.1.2 merge some fix.
1.1.1 merge some fix.
1.1.0stable fix bug:LoopViewPager position confusion;LoopViewPager non stop spinning;
1.1.0beta5 feat: TabLayoutSupport Lib supports LoopViewPager
1.1.0beta4 fix bug:support ItemDecorations;
1.1.0beta3 support reverse;
1.1.0 refactor;support TabLayout;
1.0.11 support infinite loop
1.0.10 make touch gesture smotherï¼›
1.0.8 override swapAdapterï¼›support singlePageFlingï¼›
1.0.7 remove redandunt codes; support cancel action
1.0.6 resolve potential id conflicting on FragmentViewPagerApdater
1.0.4 fix bug : exception happens if ItemView LayoutParam is not MarginLayoutParam
1.0.3 add method: getCurrentPosition
1.0.2 support FragmentViewPager, add OnPageChangedListener
1.0.1 fix bug: smoothScrollToPosition index out of range
1.0.0 reimplement RecyclerViewPager without coping RecyclerView's codes.
0.5.4 add 'HorizontalCenterLayoutManager' to implement ViewPager
0.5.3 fix bug: setHasStableIds not work
0.1.0
License
Copyright 2015 lsjwzh
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Could not find multidex.jar (com.android.support:multidex:1.0.2)
apply from: 'buildsystem/dependencies.gradle'
buildscript {
repositories {
jcenter()
mavenCentral()
maven { url 'https://maven.fabric.io/public' }
google()
//maven {
// url 'https://maven.google.com'
//}
}
dependencies {
//Update it to the latest version: 3.1.3
classpath 'com.android.tools.build:gradle:3.1.3'
classpath 'com.frogermcs.androiddevmetrics:androiddevmetrics-plugin:0.5'
classpath 'com.google.gms:google-services:3.1.2'
classpath 'io.fabric.tools:gradle:1.25.1'
}
}
allprojects {
ext {
androidApplicationId = 'com.medikoe.connect'
androidVersionCode = 23
androidVersionName = "1.7.2"
testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
testApplicationId = 'com.medikoe.test'
}
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
// Remove these lines
// repositories {
// google()
// }
-----------------------
repositories {
maven {
url 'https://maven.google.com'
}
jcenter()
// Add your other repositories here
}
repositories {
google()
jcenter()
// Add your other repositories here
}
-----------------------
repositories {
maven {
url 'https://maven.google.com'
}
jcenter()
// Add your other repositories here
}
repositories {
google()
jcenter()
// Add your other repositories here
}
-----------------------
maven { url 'https://maven.google.com' }
jcenter()
-----------------------
maven { url 'https://maven.google.com' }
jcenter()
How to implement RecyclerViewPager in kotlin?
<?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:orientation="vertical"
android:gravity="center"
tools:context=".MainActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager_2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<com.tbuonomo.viewpagerdotsindicator.WormDotsIndicator
android:id="@+id/dots_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"/>
</LinearLayout>
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupViewPager2()
}
private fun setupViewPager2() {
val viewPager2 = findViewById<ViewPager2>(R.id.view_pager_2)
val dotsIndicator = findViewById<WormDotsIndicator>(R.id.dots_indicator)
viewPager2.adapter = PagerAdapter(this)
dotsIndicator.setViewPager2(viewPager2)
}
}
class PagerAdapter(fa:FragmentActivity): FragmentStateAdapter(fa) {
override fun getItemCount(): Int = 3
override fun createFragment(position: Int): Fragment {
return when(position){
0 -> SampleFragment.newInstance("One", "desc 1")
1 -> SampleFragment.newInstance("Two", "desc 2")
2 -> SampleFragment.newInstance("Three", "desc 3")
else -> throw Exception("No such position")
}
}
}
<?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:orientation="vertical"
android:gravity="center"
tools:context=".SampleFragment">
<TextView
android:id="@+id/txt_header"
tools:text="Header 1"
android:textAlignment="center"
android:textSize="24sp"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/txt_desc"
tools:text="Desc 1"
android:textAlignment="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
class SampleFragment : Fragment() {
private var header: String? = null
private var desc: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
header = it.getString(ARG_Header)
desc = it.getString(ARG_Desc)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_sample, container, false)
setView(view)
return view
}
private fun setView(view:View){
val headerText = view.findViewById<TextView>(R.id.txt_header)
headerText.text = header
val descText = view.findViewById<TextView>(R.id.txt_desc)
descText.text = desc
}
companion object {
@JvmStatic
fun newInstance(param1: String, param2: String) =
SampleFragment().apply {
arguments = Bundle().apply {
putString(ARG_Header, param1)
putString(ARG_Desc, param2)
}
}
}
}
-----------------------
<?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:orientation="vertical"
android:gravity="center"
tools:context=".MainActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager_2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<com.tbuonomo.viewpagerdotsindicator.WormDotsIndicator
android:id="@+id/dots_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"/>
</LinearLayout>
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupViewPager2()
}
private fun setupViewPager2() {
val viewPager2 = findViewById<ViewPager2>(R.id.view_pager_2)
val dotsIndicator = findViewById<WormDotsIndicator>(R.id.dots_indicator)
viewPager2.adapter = PagerAdapter(this)
dotsIndicator.setViewPager2(viewPager2)
}
}
class PagerAdapter(fa:FragmentActivity): FragmentStateAdapter(fa) {
override fun getItemCount(): Int = 3
override fun createFragment(position: Int): Fragment {
return when(position){
0 -> SampleFragment.newInstance("One", "desc 1")
1 -> SampleFragment.newInstance("Two", "desc 2")
2 -> SampleFragment.newInstance("Three", "desc 3")
else -> throw Exception("No such position")
}
}
}
<?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:orientation="vertical"
android:gravity="center"
tools:context=".SampleFragment">
<TextView
android:id="@+id/txt_header"
tools:text="Header 1"
android:textAlignment="center"
android:textSize="24sp"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/txt_desc"
tools:text="Desc 1"
android:textAlignment="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
class SampleFragment : Fragment() {
private var header: String? = null
private var desc: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
header = it.getString(ARG_Header)
desc = it.getString(ARG_Desc)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_sample, container, false)
setView(view)
return view
}
private fun setView(view:View){
val headerText = view.findViewById<TextView>(R.id.txt_header)
headerText.text = header
val descText = view.findViewById<TextView>(R.id.txt_desc)
descText.text = desc
}
companion object {
@JvmStatic
fun newInstance(param1: String, param2: String) =
SampleFragment().apply {
arguments = Bundle().apply {
putString(ARG_Header, param1)
putString(ARG_Desc, param2)
}
}
}
}
-----------------------
<?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:orientation="vertical"
android:gravity="center"
tools:context=".MainActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager_2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<com.tbuonomo.viewpagerdotsindicator.WormDotsIndicator
android:id="@+id/dots_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"/>
</LinearLayout>
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupViewPager2()
}
private fun setupViewPager2() {
val viewPager2 = findViewById<ViewPager2>(R.id.view_pager_2)
val dotsIndicator = findViewById<WormDotsIndicator>(R.id.dots_indicator)
viewPager2.adapter = PagerAdapter(this)
dotsIndicator.setViewPager2(viewPager2)
}
}
class PagerAdapter(fa:FragmentActivity): FragmentStateAdapter(fa) {
override fun getItemCount(): Int = 3
override fun createFragment(position: Int): Fragment {
return when(position){
0 -> SampleFragment.newInstance("One", "desc 1")
1 -> SampleFragment.newInstance("Two", "desc 2")
2 -> SampleFragment.newInstance("Three", "desc 3")
else -> throw Exception("No such position")
}
}
}
<?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:orientation="vertical"
android:gravity="center"
tools:context=".SampleFragment">
<TextView
android:id="@+id/txt_header"
tools:text="Header 1"
android:textAlignment="center"
android:textSize="24sp"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/txt_desc"
tools:text="Desc 1"
android:textAlignment="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
class SampleFragment : Fragment() {
private var header: String? = null
private var desc: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
header = it.getString(ARG_Header)
desc = it.getString(ARG_Desc)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_sample, container, false)
setView(view)
return view
}
private fun setView(view:View){
val headerText = view.findViewById<TextView>(R.id.txt_header)
headerText.text = header
val descText = view.findViewById<TextView>(R.id.txt_desc)
descText.text = desc
}
companion object {
@JvmStatic
fun newInstance(param1: String, param2: String) =
SampleFragment().apply {
arguments = Bundle().apply {
putString(ARG_Header, param1)
putString(ARG_Desc, param2)
}
}
}
}
-----------------------
<?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:orientation="vertical"
android:gravity="center"
tools:context=".MainActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager_2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<com.tbuonomo.viewpagerdotsindicator.WormDotsIndicator
android:id="@+id/dots_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"/>
</LinearLayout>
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupViewPager2()
}
private fun setupViewPager2() {
val viewPager2 = findViewById<ViewPager2>(R.id.view_pager_2)
val dotsIndicator = findViewById<WormDotsIndicator>(R.id.dots_indicator)
viewPager2.adapter = PagerAdapter(this)
dotsIndicator.setViewPager2(viewPager2)
}
}
class PagerAdapter(fa:FragmentActivity): FragmentStateAdapter(fa) {
override fun getItemCount(): Int = 3
override fun createFragment(position: Int): Fragment {
return when(position){
0 -> SampleFragment.newInstance("One", "desc 1")
1 -> SampleFragment.newInstance("Two", "desc 2")
2 -> SampleFragment.newInstance("Three", "desc 3")
else -> throw Exception("No such position")
}
}
}
<?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:orientation="vertical"
android:gravity="center"
tools:context=".SampleFragment">
<TextView
android:id="@+id/txt_header"
tools:text="Header 1"
android:textAlignment="center"
android:textSize="24sp"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/txt_desc"
tools:text="Desc 1"
android:textAlignment="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
class SampleFragment : Fragment() {
private var header: String? = null
private var desc: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
header = it.getString(ARG_Header)
desc = it.getString(ARG_Desc)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_sample, container, false)
setView(view)
return view
}
private fun setView(view:View){
val headerText = view.findViewById<TextView>(R.id.txt_header)
headerText.text = header
val descText = view.findViewById<TextView>(R.id.txt_desc)
descText.text = desc
}
companion object {
@JvmStatic
fun newInstance(param1: String, param2: String) =
SampleFragment().apply {
arguments = Bundle().apply {
putString(ARG_Header, param1)
putString(ARG_Desc, param2)
}
}
}
}
-----------------------
<?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:orientation="vertical"
android:gravity="center"
tools:context=".MainActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager_2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<com.tbuonomo.viewpagerdotsindicator.WormDotsIndicator
android:id="@+id/dots_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"/>
</LinearLayout>
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupViewPager2()
}
private fun setupViewPager2() {
val viewPager2 = findViewById<ViewPager2>(R.id.view_pager_2)
val dotsIndicator = findViewById<WormDotsIndicator>(R.id.dots_indicator)
viewPager2.adapter = PagerAdapter(this)
dotsIndicator.setViewPager2(viewPager2)
}
}
class PagerAdapter(fa:FragmentActivity): FragmentStateAdapter(fa) {
override fun getItemCount(): Int = 3
override fun createFragment(position: Int): Fragment {
return when(position){
0 -> SampleFragment.newInstance("One", "desc 1")
1 -> SampleFragment.newInstance("Two", "desc 2")
2 -> SampleFragment.newInstance("Three", "desc 3")
else -> throw Exception("No such position")
}
}
}
<?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:orientation="vertical"
android:gravity="center"
tools:context=".SampleFragment">
<TextView
android:id="@+id/txt_header"
tools:text="Header 1"
android:textAlignment="center"
android:textSize="24sp"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/txt_desc"
tools:text="Desc 1"
android:textAlignment="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
class SampleFragment : Fragment() {
private var header: String? = null
private var desc: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
header = it.getString(ARG_Header)
desc = it.getString(ARG_Desc)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_sample, container, false)
setView(view)
return view
}
private fun setView(view:View){
val headerText = view.findViewById<TextView>(R.id.txt_header)
headerText.text = header
val descText = view.findViewById<TextView>(R.id.txt_desc)
descText.text = desc
}
companion object {
@JvmStatic
fun newInstance(param1: String, param2: String) =
SampleFragment().apply {
arguments = Bundle().apply {
putString(ARG_Header, param1)
putString(ARG_Desc, param2)
}
}
}
}
How to snap RecyclerView items so that every X items would be considered like a single unit to snap to?
// For LinearLayoutManager horizontal orientation
recyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false));
// For GridLayoutManager vertical orientation
recyclerView.setLayoutManager(new GridLayoutManager(this, SPAN_COUNT, RecyclerView.VERTICAL, false));
SnapToBlock snapToBlock = new SnapToBlock(mMaxFlingPages);
snapToBlock.attachToRecyclerView(recyclerView);
snapToBlock.setSnapBlockCallback(new SnapToBlock.SnapBlockCallback() {
@Override
public void onBlockSnap(int snapPosition) {
...
}
@Override
public void onBlockSnapped(int snapPosition) {
...
}
});
/* The number of items in the RecyclerView should be a multiple of block size; otherwise, the
extra item views will not be positioned on a block boundary when the end of the data is reached.
Pad out with empty item views if needed.
Updated to accommodate RTL layouts.
*/
public class SnapToBlock extends SnapHelper {
private RecyclerView mRecyclerView;
// Total number of items in a block of view in the RecyclerView
private int mBlocksize;
// Maximum number of positions to move on a fling.
private int mMaxPositionsToMove;
// Width of a RecyclerView item if orientation is horizonal; height of the item if vertical
private int mItemDimension;
// Maxim blocks to move during most vigorous fling.
private final int mMaxFlingBlocks;
// Callback interface when blocks are snapped.
private SnapBlockCallback mSnapBlockCallback;
// When snapping, used to determine direction of snap.
private int mPriorFirstPosition = RecyclerView.NO_POSITION;
// Our private scroller
private Scroller mScroller;
// Horizontal/vertical layout helper
private OrientationHelper mOrientationHelper;
// LTR/RTL helper
private LayoutDirectionHelper mLayoutDirectionHelper;
// Borrowed from ViewPager.java
private static final Interpolator sInterpolator = new Interpolator() {
public float getInterpolation(float t) {
// _o(t) = t * t * ((tension + 1) * t + tension)
// o(t) = _o(t - 1) + 1
t -= 1.0f;
return t * t * t + 1.0f;
}
};
SnapToBlock(int maxFlingBlocks) {
super();
mMaxFlingBlocks = maxFlingBlocks;
}
@Override
public void attachToRecyclerView(@Nullable final RecyclerView recyclerView)
throws IllegalStateException {
if (recyclerView != null) {
mRecyclerView = recyclerView;
final LinearLayoutManager layoutManager =
(LinearLayoutManager) recyclerView.getLayoutManager();
if (layoutManager.canScrollHorizontally()) {
mOrientationHelper = OrientationHelper.createHorizontalHelper(layoutManager);
mLayoutDirectionHelper =
new LayoutDirectionHelper(ViewCompat.getLayoutDirection(mRecyclerView));
} else if (layoutManager.canScrollVertically()) {
mOrientationHelper = OrientationHelper.createVerticalHelper(layoutManager);
// RTL doesn't matter for vertical scrolling for this class.
mLayoutDirectionHelper = new LayoutDirectionHelper(RecyclerView.LAYOUT_DIRECTION_LTR);
} else {
throw new IllegalStateException("RecyclerView must be scrollable");
}
mScroller = new Scroller(mRecyclerView.getContext(), sInterpolator);
initItemDimensionIfNeeded(layoutManager);
}
super.attachToRecyclerView(recyclerView);
}
// Called when the target view is available and we need to know how much more
// to scroll to get it lined up with the side of the RecyclerView.
@NonNull
@Override
public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager,
@NonNull View targetView) {
int[] out = new int[2];
if (layoutManager.canScrollHorizontally()) {
out[0] = mLayoutDirectionHelper.getScrollToAlignView(targetView);
}
if (layoutManager.canScrollVertically()) {
out[1] = mLayoutDirectionHelper.getScrollToAlignView(targetView);
}
if (mSnapBlockCallback != null) {
if (out[0] == 0 && out[1] == 0) {
mSnapBlockCallback.onBlockSnapped(layoutManager.getPosition(targetView));
} else {
mSnapBlockCallback.onBlockSnap(layoutManager.getPosition(targetView));
}
}
return out;
}
// We are flinging and need to know where we are heading.
@Override
public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager,
int velocityX, int velocityY) {
LinearLayoutManager lm = (LinearLayoutManager) layoutManager;
initItemDimensionIfNeeded(layoutManager);
mScroller.fling(0, 0, velocityX, velocityY, Integer.MIN_VALUE, Integer.MAX_VALUE,
Integer.MIN_VALUE, Integer.MAX_VALUE);
if (velocityX != 0) {
return mLayoutDirectionHelper
.getPositionsToMove(lm, mScroller.getFinalX(), mItemDimension);
}
if (velocityY != 0) {
return mLayoutDirectionHelper
.getPositionsToMove(lm, mScroller.getFinalY(), mItemDimension);
}
return RecyclerView.NO_POSITION;
}
// We have scrolled to the neighborhood where we will snap. Determine the snap position.
@Override
public View findSnapView(RecyclerView.LayoutManager layoutManager) {
// Snap to a view that is either 1) toward the bottom of the data and therefore on screen,
// or, 2) toward the top of the data and may be off-screen.
int snapPos = calcTargetPosition((LinearLayoutManager) layoutManager);
View snapView = (snapPos == RecyclerView.NO_POSITION)
? null : layoutManager.findViewByPosition(snapPos);
if (snapView == null) {
Log.d(TAG, "<<<<findSnapView is returning null!");
}
Log.d(TAG, "<<<<findSnapView snapos=" + snapPos);
return snapView;
}
// Does the heavy lifting for findSnapView.
private int calcTargetPosition(LinearLayoutManager layoutManager) {
int snapPos;
int firstVisiblePos = layoutManager.findFirstVisibleItemPosition();
if (firstVisiblePos == RecyclerView.NO_POSITION) {
return RecyclerView.NO_POSITION;
}
initItemDimensionIfNeeded(layoutManager);
if (firstVisiblePos >= mPriorFirstPosition) {
// Scrolling toward bottom of data
int firstCompletePosition = layoutManager.findFirstCompletelyVisibleItemPosition();
if (firstCompletePosition != RecyclerView.NO_POSITION
&& firstCompletePosition % mBlocksize == 0) {
snapPos = firstCompletePosition;
} else {
snapPos = roundDownToBlockSize(firstVisiblePos + mBlocksize);
}
} else {
// Scrolling toward top of data
snapPos = roundDownToBlockSize(firstVisiblePos);
// Check to see if target view exists. If it doesn't, force a smooth scroll.
// SnapHelper only snaps to existing views and will not scroll to a non-existant one.
// If limiting fling to single block, then the following is not needed since the
// views are likely to be in the RecyclerView pool.
if (layoutManager.findViewByPosition(snapPos) == null) {
int[] toScroll = mLayoutDirectionHelper.calculateDistanceToScroll(layoutManager, snapPos);
mRecyclerView.smoothScrollBy(toScroll[0], toScroll[1], sInterpolator);
}
}
mPriorFirstPosition = firstVisiblePos;
return snapPos;
}
private void initItemDimensionIfNeeded(final RecyclerView.LayoutManager layoutManager) {
if (mItemDimension != 0) {
return;
}
View child;
if ((child = layoutManager.getChildAt(0)) == null) {
return;
}
if (layoutManager.canScrollHorizontally()) {
mItemDimension = child.getWidth();
mBlocksize = getSpanCount(layoutManager) * (mRecyclerView.getWidth() / mItemDimension);
} else if (layoutManager.canScrollVertically()) {
mItemDimension = child.getHeight();
mBlocksize = getSpanCount(layoutManager) * (mRecyclerView.getHeight() / mItemDimension);
}
mMaxPositionsToMove = mBlocksize * mMaxFlingBlocks;
}
private int getSpanCount(RecyclerView.LayoutManager layoutManager) {
return (layoutManager instanceof GridLayoutManager)
? ((GridLayoutManager) layoutManager).getSpanCount()
: 1;
}
private int roundDownToBlockSize(int trialPosition) {
return trialPosition - trialPosition % mBlocksize;
}
private int roundUpToBlockSize(int trialPosition) {
return roundDownToBlockSize(trialPosition + mBlocksize - 1);
}
@Nullable
protected LinearSmoothScroller createScroller(RecyclerView.LayoutManager layoutManager) {
if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
return null;
}
return new LinearSmoothScroller(mRecyclerView.getContext()) {
@Override
protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
int[] snapDistances = calculateDistanceToFinalSnap(mRecyclerView.getLayoutManager(),
targetView);
final int dx = snapDistances[0];
final int dy = snapDistances[1];
final int time = calculateTimeForDeceleration(Math.max(Math.abs(dx), Math.abs(dy)));
if (time > 0) {
action.update(dx, dy, time, sInterpolator);
}
}
@Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
}
};
}
public void setSnapBlockCallback(@Nullable SnapBlockCallback callback) {
mSnapBlockCallback = callback;
}
/*
Helper class that handles calculations for LTR and RTL layouts.
*/
private class LayoutDirectionHelper {
// Is the layout an RTL one?
private final boolean mIsRTL;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
LayoutDirectionHelper(int direction) {
mIsRTL = direction == View.LAYOUT_DIRECTION_RTL;
}
/*
Calculate the amount of scroll needed to align the target view with the layout edge.
*/
int getScrollToAlignView(View targetView) {
return (mIsRTL)
? mOrientationHelper.getDecoratedEnd(targetView) - mRecyclerView.getWidth()
: mOrientationHelper.getDecoratedStart(targetView);
}
/**
* Calculate the distance to final snap position when the view corresponding to the snap
* position is not currently available.
*
* @param layoutManager LinearLayoutManager or descendent class
* @param targetPos - Adapter position to snap to
* @return int[2] {x-distance in pixels, y-distance in pixels}
*/
int[] calculateDistanceToScroll(LinearLayoutManager layoutManager, int targetPos) {
int[] out = new int[2];
int firstVisiblePos;
firstVisiblePos = layoutManager.findFirstVisibleItemPosition();
if (layoutManager.canScrollHorizontally()) {
if (targetPos <= firstVisiblePos) { // scrolling toward top of data
if (mIsRTL) {
View lastView = layoutManager.findViewByPosition(layoutManager.findLastVisibleItemPosition());
out[0] = mOrientationHelper.getDecoratedEnd(lastView)
+ (firstVisiblePos - targetPos) * mItemDimension;
} else {
View firstView = layoutManager.findViewByPosition(firstVisiblePos);
out[0] = mOrientationHelper.getDecoratedStart(firstView)
- (firstVisiblePos - targetPos) * mItemDimension;
}
}
}
if (layoutManager.canScrollVertically()) {
if (targetPos <= firstVisiblePos) { // scrolling toward top of data
View firstView = layoutManager.findViewByPosition(firstVisiblePos);
out[1] = firstView.getTop() - (firstVisiblePos - targetPos) * mItemDimension;
}
}
return out;
}
/*
Calculate the number of positions to move in the RecyclerView given a scroll amount
and the size of the items to be scrolled. Return integral multiple of mBlockSize not
equal to zero.
*/
int getPositionsToMove(LinearLayoutManager llm, int scroll, int itemSize) {
int positionsToMove;
positionsToMove = roundUpToBlockSize(Math.abs(scroll) / itemSize);
if (positionsToMove < mBlocksize) {
// Must move at least one block
positionsToMove = mBlocksize;
} else if (positionsToMove > mMaxPositionsToMove) {
// Clamp number of positions to move so we don't get wild flinging.
positionsToMove = mMaxPositionsToMove;
}
if (scroll < 0) {
positionsToMove *= -1;
}
if (mIsRTL) {
positionsToMove *= -1;
}
if (mLayoutDirectionHelper.isDirectionToBottom(scroll < 0)) {
// Scrolling toward the bottom of data.
return roundDownToBlockSize(llm.findFirstVisibleItemPosition()) + positionsToMove;
}
// Scrolling toward the top of the data.
return roundDownToBlockSize(llm.findLastVisibleItemPosition()) + positionsToMove;
}
boolean isDirectionToBottom(boolean velocityNegative) {
//noinspection SimplifiableConditionalExpression
return mIsRTL ? velocityNegative : !velocityNegative;
}
}
public interface SnapBlockCallback {
void onBlockSnap(int snapPosition);
void onBlockSnapped(int snapPosition);
}
private static final float MILLISECONDS_PER_INCH = 100f;
@SuppressWarnings("unused")
private static final String TAG = "SnapToBlock";
}
-----------------------
// For LinearLayoutManager horizontal orientation
recyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false));
// For GridLayoutManager vertical orientation
recyclerView.setLayoutManager(new GridLayoutManager(this, SPAN_COUNT, RecyclerView.VERTICAL, false));
SnapToBlock snapToBlock = new SnapToBlock(mMaxFlingPages);
snapToBlock.attachToRecyclerView(recyclerView);
snapToBlock.setSnapBlockCallback(new SnapToBlock.SnapBlockCallback() {
@Override
public void onBlockSnap(int snapPosition) {
...
}
@Override
public void onBlockSnapped(int snapPosition) {
...
}
});
/* The number of items in the RecyclerView should be a multiple of block size; otherwise, the
extra item views will not be positioned on a block boundary when the end of the data is reached.
Pad out with empty item views if needed.
Updated to accommodate RTL layouts.
*/
public class SnapToBlock extends SnapHelper {
private RecyclerView mRecyclerView;
// Total number of items in a block of view in the RecyclerView
private int mBlocksize;
// Maximum number of positions to move on a fling.
private int mMaxPositionsToMove;
// Width of a RecyclerView item if orientation is horizonal; height of the item if vertical
private int mItemDimension;
// Maxim blocks to move during most vigorous fling.
private final int mMaxFlingBlocks;
// Callback interface when blocks are snapped.
private SnapBlockCallback mSnapBlockCallback;
// When snapping, used to determine direction of snap.
private int mPriorFirstPosition = RecyclerView.NO_POSITION;
// Our private scroller
private Scroller mScroller;
// Horizontal/vertical layout helper
private OrientationHelper mOrientationHelper;
// LTR/RTL helper
private LayoutDirectionHelper mLayoutDirectionHelper;
// Borrowed from ViewPager.java
private static final Interpolator sInterpolator = new Interpolator() {
public float getInterpolation(float t) {
// _o(t) = t * t * ((tension + 1) * t + tension)
// o(t) = _o(t - 1) + 1
t -= 1.0f;
return t * t * t + 1.0f;
}
};
SnapToBlock(int maxFlingBlocks) {
super();
mMaxFlingBlocks = maxFlingBlocks;
}
@Override
public void attachToRecyclerView(@Nullable final RecyclerView recyclerView)
throws IllegalStateException {
if (recyclerView != null) {
mRecyclerView = recyclerView;
final LinearLayoutManager layoutManager =
(LinearLayoutManager) recyclerView.getLayoutManager();
if (layoutManager.canScrollHorizontally()) {
mOrientationHelper = OrientationHelper.createHorizontalHelper(layoutManager);
mLayoutDirectionHelper =
new LayoutDirectionHelper(ViewCompat.getLayoutDirection(mRecyclerView));
} else if (layoutManager.canScrollVertically()) {
mOrientationHelper = OrientationHelper.createVerticalHelper(layoutManager);
// RTL doesn't matter for vertical scrolling for this class.
mLayoutDirectionHelper = new LayoutDirectionHelper(RecyclerView.LAYOUT_DIRECTION_LTR);
} else {
throw new IllegalStateException("RecyclerView must be scrollable");
}
mScroller = new Scroller(mRecyclerView.getContext(), sInterpolator);
initItemDimensionIfNeeded(layoutManager);
}
super.attachToRecyclerView(recyclerView);
}
// Called when the target view is available and we need to know how much more
// to scroll to get it lined up with the side of the RecyclerView.
@NonNull
@Override
public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager,
@NonNull View targetView) {
int[] out = new int[2];
if (layoutManager.canScrollHorizontally()) {
out[0] = mLayoutDirectionHelper.getScrollToAlignView(targetView);
}
if (layoutManager.canScrollVertically()) {
out[1] = mLayoutDirectionHelper.getScrollToAlignView(targetView);
}
if (mSnapBlockCallback != null) {
if (out[0] == 0 && out[1] == 0) {
mSnapBlockCallback.onBlockSnapped(layoutManager.getPosition(targetView));
} else {
mSnapBlockCallback.onBlockSnap(layoutManager.getPosition(targetView));
}
}
return out;
}
// We are flinging and need to know where we are heading.
@Override
public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager,
int velocityX, int velocityY) {
LinearLayoutManager lm = (LinearLayoutManager) layoutManager;
initItemDimensionIfNeeded(layoutManager);
mScroller.fling(0, 0, velocityX, velocityY, Integer.MIN_VALUE, Integer.MAX_VALUE,
Integer.MIN_VALUE, Integer.MAX_VALUE);
if (velocityX != 0) {
return mLayoutDirectionHelper
.getPositionsToMove(lm, mScroller.getFinalX(), mItemDimension);
}
if (velocityY != 0) {
return mLayoutDirectionHelper
.getPositionsToMove(lm, mScroller.getFinalY(), mItemDimension);
}
return RecyclerView.NO_POSITION;
}
// We have scrolled to the neighborhood where we will snap. Determine the snap position.
@Override
public View findSnapView(RecyclerView.LayoutManager layoutManager) {
// Snap to a view that is either 1) toward the bottom of the data and therefore on screen,
// or, 2) toward the top of the data and may be off-screen.
int snapPos = calcTargetPosition((LinearLayoutManager) layoutManager);
View snapView = (snapPos == RecyclerView.NO_POSITION)
? null : layoutManager.findViewByPosition(snapPos);
if (snapView == null) {
Log.d(TAG, "<<<<findSnapView is returning null!");
}
Log.d(TAG, "<<<<findSnapView snapos=" + snapPos);
return snapView;
}
// Does the heavy lifting for findSnapView.
private int calcTargetPosition(LinearLayoutManager layoutManager) {
int snapPos;
int firstVisiblePos = layoutManager.findFirstVisibleItemPosition();
if (firstVisiblePos == RecyclerView.NO_POSITION) {
return RecyclerView.NO_POSITION;
}
initItemDimensionIfNeeded(layoutManager);
if (firstVisiblePos >= mPriorFirstPosition) {
// Scrolling toward bottom of data
int firstCompletePosition = layoutManager.findFirstCompletelyVisibleItemPosition();
if (firstCompletePosition != RecyclerView.NO_POSITION
&& firstCompletePosition % mBlocksize == 0) {
snapPos = firstCompletePosition;
} else {
snapPos = roundDownToBlockSize(firstVisiblePos + mBlocksize);
}
} else {
// Scrolling toward top of data
snapPos = roundDownToBlockSize(firstVisiblePos);
// Check to see if target view exists. If it doesn't, force a smooth scroll.
// SnapHelper only snaps to existing views and will not scroll to a non-existant one.
// If limiting fling to single block, then the following is not needed since the
// views are likely to be in the RecyclerView pool.
if (layoutManager.findViewByPosition(snapPos) == null) {
int[] toScroll = mLayoutDirectionHelper.calculateDistanceToScroll(layoutManager, snapPos);
mRecyclerView.smoothScrollBy(toScroll[0], toScroll[1], sInterpolator);
}
}
mPriorFirstPosition = firstVisiblePos;
return snapPos;
}
private void initItemDimensionIfNeeded(final RecyclerView.LayoutManager layoutManager) {
if (mItemDimension != 0) {
return;
}
View child;
if ((child = layoutManager.getChildAt(0)) == null) {
return;
}
if (layoutManager.canScrollHorizontally()) {
mItemDimension = child.getWidth();
mBlocksize = getSpanCount(layoutManager) * (mRecyclerView.getWidth() / mItemDimension);
} else if (layoutManager.canScrollVertically()) {
mItemDimension = child.getHeight();
mBlocksize = getSpanCount(layoutManager) * (mRecyclerView.getHeight() / mItemDimension);
}
mMaxPositionsToMove = mBlocksize * mMaxFlingBlocks;
}
private int getSpanCount(RecyclerView.LayoutManager layoutManager) {
return (layoutManager instanceof GridLayoutManager)
? ((GridLayoutManager) layoutManager).getSpanCount()
: 1;
}
private int roundDownToBlockSize(int trialPosition) {
return trialPosition - trialPosition % mBlocksize;
}
private int roundUpToBlockSize(int trialPosition) {
return roundDownToBlockSize(trialPosition + mBlocksize - 1);
}
@Nullable
protected LinearSmoothScroller createScroller(RecyclerView.LayoutManager layoutManager) {
if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
return null;
}
return new LinearSmoothScroller(mRecyclerView.getContext()) {
@Override
protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
int[] snapDistances = calculateDistanceToFinalSnap(mRecyclerView.getLayoutManager(),
targetView);
final int dx = snapDistances[0];
final int dy = snapDistances[1];
final int time = calculateTimeForDeceleration(Math.max(Math.abs(dx), Math.abs(dy)));
if (time > 0) {
action.update(dx, dy, time, sInterpolator);
}
}
@Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
}
};
}
public void setSnapBlockCallback(@Nullable SnapBlockCallback callback) {
mSnapBlockCallback = callback;
}
/*
Helper class that handles calculations for LTR and RTL layouts.
*/
private class LayoutDirectionHelper {
// Is the layout an RTL one?
private final boolean mIsRTL;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
LayoutDirectionHelper(int direction) {
mIsRTL = direction == View.LAYOUT_DIRECTION_RTL;
}
/*
Calculate the amount of scroll needed to align the target view with the layout edge.
*/
int getScrollToAlignView(View targetView) {
return (mIsRTL)
? mOrientationHelper.getDecoratedEnd(targetView) - mRecyclerView.getWidth()
: mOrientationHelper.getDecoratedStart(targetView);
}
/**
* Calculate the distance to final snap position when the view corresponding to the snap
* position is not currently available.
*
* @param layoutManager LinearLayoutManager or descendent class
* @param targetPos - Adapter position to snap to
* @return int[2] {x-distance in pixels, y-distance in pixels}
*/
int[] calculateDistanceToScroll(LinearLayoutManager layoutManager, int targetPos) {
int[] out = new int[2];
int firstVisiblePos;
firstVisiblePos = layoutManager.findFirstVisibleItemPosition();
if (layoutManager.canScrollHorizontally()) {
if (targetPos <= firstVisiblePos) { // scrolling toward top of data
if (mIsRTL) {
View lastView = layoutManager.findViewByPosition(layoutManager.findLastVisibleItemPosition());
out[0] = mOrientationHelper.getDecoratedEnd(lastView)
+ (firstVisiblePos - targetPos) * mItemDimension;
} else {
View firstView = layoutManager.findViewByPosition(firstVisiblePos);
out[0] = mOrientationHelper.getDecoratedStart(firstView)
- (firstVisiblePos - targetPos) * mItemDimension;
}
}
}
if (layoutManager.canScrollVertically()) {
if (targetPos <= firstVisiblePos) { // scrolling toward top of data
View firstView = layoutManager.findViewByPosition(firstVisiblePos);
out[1] = firstView.getTop() - (firstVisiblePos - targetPos) * mItemDimension;
}
}
return out;
}
/*
Calculate the number of positions to move in the RecyclerView given a scroll amount
and the size of the items to be scrolled. Return integral multiple of mBlockSize not
equal to zero.
*/
int getPositionsToMove(LinearLayoutManager llm, int scroll, int itemSize) {
int positionsToMove;
positionsToMove = roundUpToBlockSize(Math.abs(scroll) / itemSize);
if (positionsToMove < mBlocksize) {
// Must move at least one block
positionsToMove = mBlocksize;
} else if (positionsToMove > mMaxPositionsToMove) {
// Clamp number of positions to move so we don't get wild flinging.
positionsToMove = mMaxPositionsToMove;
}
if (scroll < 0) {
positionsToMove *= -1;
}
if (mIsRTL) {
positionsToMove *= -1;
}
if (mLayoutDirectionHelper.isDirectionToBottom(scroll < 0)) {
// Scrolling toward the bottom of data.
return roundDownToBlockSize(llm.findFirstVisibleItemPosition()) + positionsToMove;
}
// Scrolling toward the top of the data.
return roundDownToBlockSize(llm.findLastVisibleItemPosition()) + positionsToMove;
}
boolean isDirectionToBottom(boolean velocityNegative) {
//noinspection SimplifiableConditionalExpression
return mIsRTL ? velocityNegative : !velocityNegative;
}
}
public interface SnapBlockCallback {
void onBlockSnap(int snapPosition);
void onBlockSnapped(int snapPosition);
}
private static final float MILLISECONDS_PER_INCH = 100f;
@SuppressWarnings("unused")
private static final String TAG = "SnapToBlock";
}
-----------------------
// For LinearLayoutManager horizontal orientation
recyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false));
// For GridLayoutManager vertical orientation
recyclerView.setLayoutManager(new GridLayoutManager(this, SPAN_COUNT, RecyclerView.VERTICAL, false));
SnapToBlock snapToBlock = new SnapToBlock(mMaxFlingPages);
snapToBlock.attachToRecyclerView(recyclerView);
snapToBlock.setSnapBlockCallback(new SnapToBlock.SnapBlockCallback() {
@Override
public void onBlockSnap(int snapPosition) {
...
}
@Override
public void onBlockSnapped(int snapPosition) {
...
}
});
/* The number of items in the RecyclerView should be a multiple of block size; otherwise, the
extra item views will not be positioned on a block boundary when the end of the data is reached.
Pad out with empty item views if needed.
Updated to accommodate RTL layouts.
*/
public class SnapToBlock extends SnapHelper {
private RecyclerView mRecyclerView;
// Total number of items in a block of view in the RecyclerView
private int mBlocksize;
// Maximum number of positions to move on a fling.
private int mMaxPositionsToMove;
// Width of a RecyclerView item if orientation is horizonal; height of the item if vertical
private int mItemDimension;
// Maxim blocks to move during most vigorous fling.
private final int mMaxFlingBlocks;
// Callback interface when blocks are snapped.
private SnapBlockCallback mSnapBlockCallback;
// When snapping, used to determine direction of snap.
private int mPriorFirstPosition = RecyclerView.NO_POSITION;
// Our private scroller
private Scroller mScroller;
// Horizontal/vertical layout helper
private OrientationHelper mOrientationHelper;
// LTR/RTL helper
private LayoutDirectionHelper mLayoutDirectionHelper;
// Borrowed from ViewPager.java
private static final Interpolator sInterpolator = new Interpolator() {
public float getInterpolation(float t) {
// _o(t) = t * t * ((tension + 1) * t + tension)
// o(t) = _o(t - 1) + 1
t -= 1.0f;
return t * t * t + 1.0f;
}
};
SnapToBlock(int maxFlingBlocks) {
super();
mMaxFlingBlocks = maxFlingBlocks;
}
@Override
public void attachToRecyclerView(@Nullable final RecyclerView recyclerView)
throws IllegalStateException {
if (recyclerView != null) {
mRecyclerView = recyclerView;
final LinearLayoutManager layoutManager =
(LinearLayoutManager) recyclerView.getLayoutManager();
if (layoutManager.canScrollHorizontally()) {
mOrientationHelper = OrientationHelper.createHorizontalHelper(layoutManager);
mLayoutDirectionHelper =
new LayoutDirectionHelper(ViewCompat.getLayoutDirection(mRecyclerView));
} else if (layoutManager.canScrollVertically()) {
mOrientationHelper = OrientationHelper.createVerticalHelper(layoutManager);
// RTL doesn't matter for vertical scrolling for this class.
mLayoutDirectionHelper = new LayoutDirectionHelper(RecyclerView.LAYOUT_DIRECTION_LTR);
} else {
throw new IllegalStateException("RecyclerView must be scrollable");
}
mScroller = new Scroller(mRecyclerView.getContext(), sInterpolator);
initItemDimensionIfNeeded(layoutManager);
}
super.attachToRecyclerView(recyclerView);
}
// Called when the target view is available and we need to know how much more
// to scroll to get it lined up with the side of the RecyclerView.
@NonNull
@Override
public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager,
@NonNull View targetView) {
int[] out = new int[2];
if (layoutManager.canScrollHorizontally()) {
out[0] = mLayoutDirectionHelper.getScrollToAlignView(targetView);
}
if (layoutManager.canScrollVertically()) {
out[1] = mLayoutDirectionHelper.getScrollToAlignView(targetView);
}
if (mSnapBlockCallback != null) {
if (out[0] == 0 && out[1] == 0) {
mSnapBlockCallback.onBlockSnapped(layoutManager.getPosition(targetView));
} else {
mSnapBlockCallback.onBlockSnap(layoutManager.getPosition(targetView));
}
}
return out;
}
// We are flinging and need to know where we are heading.
@Override
public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager,
int velocityX, int velocityY) {
LinearLayoutManager lm = (LinearLayoutManager) layoutManager;
initItemDimensionIfNeeded(layoutManager);
mScroller.fling(0, 0, velocityX, velocityY, Integer.MIN_VALUE, Integer.MAX_VALUE,
Integer.MIN_VALUE, Integer.MAX_VALUE);
if (velocityX != 0) {
return mLayoutDirectionHelper
.getPositionsToMove(lm, mScroller.getFinalX(), mItemDimension);
}
if (velocityY != 0) {
return mLayoutDirectionHelper
.getPositionsToMove(lm, mScroller.getFinalY(), mItemDimension);
}
return RecyclerView.NO_POSITION;
}
// We have scrolled to the neighborhood where we will snap. Determine the snap position.
@Override
public View findSnapView(RecyclerView.LayoutManager layoutManager) {
// Snap to a view that is either 1) toward the bottom of the data and therefore on screen,
// or, 2) toward the top of the data and may be off-screen.
int snapPos = calcTargetPosition((LinearLayoutManager) layoutManager);
View snapView = (snapPos == RecyclerView.NO_POSITION)
? null : layoutManager.findViewByPosition(snapPos);
if (snapView == null) {
Log.d(TAG, "<<<<findSnapView is returning null!");
}
Log.d(TAG, "<<<<findSnapView snapos=" + snapPos);
return snapView;
}
// Does the heavy lifting for findSnapView.
private int calcTargetPosition(LinearLayoutManager layoutManager) {
int snapPos;
int firstVisiblePos = layoutManager.findFirstVisibleItemPosition();
if (firstVisiblePos == RecyclerView.NO_POSITION) {
return RecyclerView.NO_POSITION;
}
initItemDimensionIfNeeded(layoutManager);
if (firstVisiblePos >= mPriorFirstPosition) {
// Scrolling toward bottom of data
int firstCompletePosition = layoutManager.findFirstCompletelyVisibleItemPosition();
if (firstCompletePosition != RecyclerView.NO_POSITION
&& firstCompletePosition % mBlocksize == 0) {
snapPos = firstCompletePosition;
} else {
snapPos = roundDownToBlockSize(firstVisiblePos + mBlocksize);
}
} else {
// Scrolling toward top of data
snapPos = roundDownToBlockSize(firstVisiblePos);
// Check to see if target view exists. If it doesn't, force a smooth scroll.
// SnapHelper only snaps to existing views and will not scroll to a non-existant one.
// If limiting fling to single block, then the following is not needed since the
// views are likely to be in the RecyclerView pool.
if (layoutManager.findViewByPosition(snapPos) == null) {
int[] toScroll = mLayoutDirectionHelper.calculateDistanceToScroll(layoutManager, snapPos);
mRecyclerView.smoothScrollBy(toScroll[0], toScroll[1], sInterpolator);
}
}
mPriorFirstPosition = firstVisiblePos;
return snapPos;
}
private void initItemDimensionIfNeeded(final RecyclerView.LayoutManager layoutManager) {
if (mItemDimension != 0) {
return;
}
View child;
if ((child = layoutManager.getChildAt(0)) == null) {
return;
}
if (layoutManager.canScrollHorizontally()) {
mItemDimension = child.getWidth();
mBlocksize = getSpanCount(layoutManager) * (mRecyclerView.getWidth() / mItemDimension);
} else if (layoutManager.canScrollVertically()) {
mItemDimension = child.getHeight();
mBlocksize = getSpanCount(layoutManager) * (mRecyclerView.getHeight() / mItemDimension);
}
mMaxPositionsToMove = mBlocksize * mMaxFlingBlocks;
}
private int getSpanCount(RecyclerView.LayoutManager layoutManager) {
return (layoutManager instanceof GridLayoutManager)
? ((GridLayoutManager) layoutManager).getSpanCount()
: 1;
}
private int roundDownToBlockSize(int trialPosition) {
return trialPosition - trialPosition % mBlocksize;
}
private int roundUpToBlockSize(int trialPosition) {
return roundDownToBlockSize(trialPosition + mBlocksize - 1);
}
@Nullable
protected LinearSmoothScroller createScroller(RecyclerView.LayoutManager layoutManager) {
if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
return null;
}
return new LinearSmoothScroller(mRecyclerView.getContext()) {
@Override
protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
int[] snapDistances = calculateDistanceToFinalSnap(mRecyclerView.getLayoutManager(),
targetView);
final int dx = snapDistances[0];
final int dy = snapDistances[1];
final int time = calculateTimeForDeceleration(Math.max(Math.abs(dx), Math.abs(dy)));
if (time > 0) {
action.update(dx, dy, time, sInterpolator);
}
}
@Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
}
};
}
public void setSnapBlockCallback(@Nullable SnapBlockCallback callback) {
mSnapBlockCallback = callback;
}
/*
Helper class that handles calculations for LTR and RTL layouts.
*/
private class LayoutDirectionHelper {
// Is the layout an RTL one?
private final boolean mIsRTL;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
LayoutDirectionHelper(int direction) {
mIsRTL = direction == View.LAYOUT_DIRECTION_RTL;
}
/*
Calculate the amount of scroll needed to align the target view with the layout edge.
*/
int getScrollToAlignView(View targetView) {
return (mIsRTL)
? mOrientationHelper.getDecoratedEnd(targetView) - mRecyclerView.getWidth()
: mOrientationHelper.getDecoratedStart(targetView);
}
/**
* Calculate the distance to final snap position when the view corresponding to the snap
* position is not currently available.
*
* @param layoutManager LinearLayoutManager or descendent class
* @param targetPos - Adapter position to snap to
* @return int[2] {x-distance in pixels, y-distance in pixels}
*/
int[] calculateDistanceToScroll(LinearLayoutManager layoutManager, int targetPos) {
int[] out = new int[2];
int firstVisiblePos;
firstVisiblePos = layoutManager.findFirstVisibleItemPosition();
if (layoutManager.canScrollHorizontally()) {
if (targetPos <= firstVisiblePos) { // scrolling toward top of data
if (mIsRTL) {
View lastView = layoutManager.findViewByPosition(layoutManager.findLastVisibleItemPosition());
out[0] = mOrientationHelper.getDecoratedEnd(lastView)
+ (firstVisiblePos - targetPos) * mItemDimension;
} else {
View firstView = layoutManager.findViewByPosition(firstVisiblePos);
out[0] = mOrientationHelper.getDecoratedStart(firstView)
- (firstVisiblePos - targetPos) * mItemDimension;
}
}
}
if (layoutManager.canScrollVertically()) {
if (targetPos <= firstVisiblePos) { // scrolling toward top of data
View firstView = layoutManager.findViewByPosition(firstVisiblePos);
out[1] = firstView.getTop() - (firstVisiblePos - targetPos) * mItemDimension;
}
}
return out;
}
/*
Calculate the number of positions to move in the RecyclerView given a scroll amount
and the size of the items to be scrolled. Return integral multiple of mBlockSize not
equal to zero.
*/
int getPositionsToMove(LinearLayoutManager llm, int scroll, int itemSize) {
int positionsToMove;
positionsToMove = roundUpToBlockSize(Math.abs(scroll) / itemSize);
if (positionsToMove < mBlocksize) {
// Must move at least one block
positionsToMove = mBlocksize;
} else if (positionsToMove > mMaxPositionsToMove) {
// Clamp number of positions to move so we don't get wild flinging.
positionsToMove = mMaxPositionsToMove;
}
if (scroll < 0) {
positionsToMove *= -1;
}
if (mIsRTL) {
positionsToMove *= -1;
}
if (mLayoutDirectionHelper.isDirectionToBottom(scroll < 0)) {
// Scrolling toward the bottom of data.
return roundDownToBlockSize(llm.findFirstVisibleItemPosition()) + positionsToMove;
}
// Scrolling toward the top of the data.
return roundDownToBlockSize(llm.findLastVisibleItemPosition()) + positionsToMove;
}
boolean isDirectionToBottom(boolean velocityNegative) {
//noinspection SimplifiableConditionalExpression
return mIsRTL ? velocityNegative : !velocityNegative;
}
}
public interface SnapBlockCallback {
void onBlockSnap(int snapPosition);
void onBlockSnapped(int snapPosition);
}
private static final float MILLISECONDS_PER_INCH = 100f;
@SuppressWarnings("unused")
private static final String TAG = "SnapToBlock";
}
-----------------------
// For LinearLayoutManager horizontal orientation
recyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false));
// For GridLayoutManager vertical orientation
recyclerView.setLayoutManager(new GridLayoutManager(this, SPAN_COUNT, RecyclerView.VERTICAL, false));
SnapToBlock snapToBlock = new SnapToBlock(mMaxFlingPages);
snapToBlock.attachToRecyclerView(recyclerView);
snapToBlock.setSnapBlockCallback(new SnapToBlock.SnapBlockCallback() {
@Override
public void onBlockSnap(int snapPosition) {
...
}
@Override
public void onBlockSnapped(int snapPosition) {
...
}
});
/* The number of items in the RecyclerView should be a multiple of block size; otherwise, the
extra item views will not be positioned on a block boundary when the end of the data is reached.
Pad out with empty item views if needed.
Updated to accommodate RTL layouts.
*/
public class SnapToBlock extends SnapHelper {
private RecyclerView mRecyclerView;
// Total number of items in a block of view in the RecyclerView
private int mBlocksize;
// Maximum number of positions to move on a fling.
private int mMaxPositionsToMove;
// Width of a RecyclerView item if orientation is horizonal; height of the item if vertical
private int mItemDimension;
// Maxim blocks to move during most vigorous fling.
private final int mMaxFlingBlocks;
// Callback interface when blocks are snapped.
private SnapBlockCallback mSnapBlockCallback;
// When snapping, used to determine direction of snap.
private int mPriorFirstPosition = RecyclerView.NO_POSITION;
// Our private scroller
private Scroller mScroller;
// Horizontal/vertical layout helper
private OrientationHelper mOrientationHelper;
// LTR/RTL helper
private LayoutDirectionHelper mLayoutDirectionHelper;
// Borrowed from ViewPager.java
private static final Interpolator sInterpolator = new Interpolator() {
public float getInterpolation(float t) {
// _o(t) = t * t * ((tension + 1) * t + tension)
// o(t) = _o(t - 1) + 1
t -= 1.0f;
return t * t * t + 1.0f;
}
};
SnapToBlock(int maxFlingBlocks) {
super();
mMaxFlingBlocks = maxFlingBlocks;
}
@Override
public void attachToRecyclerView(@Nullable final RecyclerView recyclerView)
throws IllegalStateException {
if (recyclerView != null) {
mRecyclerView = recyclerView;
final LinearLayoutManager layoutManager =
(LinearLayoutManager) recyclerView.getLayoutManager();
if (layoutManager.canScrollHorizontally()) {
mOrientationHelper = OrientationHelper.createHorizontalHelper(layoutManager);
mLayoutDirectionHelper =
new LayoutDirectionHelper(ViewCompat.getLayoutDirection(mRecyclerView));
} else if (layoutManager.canScrollVertically()) {
mOrientationHelper = OrientationHelper.createVerticalHelper(layoutManager);
// RTL doesn't matter for vertical scrolling for this class.
mLayoutDirectionHelper = new LayoutDirectionHelper(RecyclerView.LAYOUT_DIRECTION_LTR);
} else {
throw new IllegalStateException("RecyclerView must be scrollable");
}
mScroller = new Scroller(mRecyclerView.getContext(), sInterpolator);
initItemDimensionIfNeeded(layoutManager);
}
super.attachToRecyclerView(recyclerView);
}
// Called when the target view is available and we need to know how much more
// to scroll to get it lined up with the side of the RecyclerView.
@NonNull
@Override
public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager,
@NonNull View targetView) {
int[] out = new int[2];
if (layoutManager.canScrollHorizontally()) {
out[0] = mLayoutDirectionHelper.getScrollToAlignView(targetView);
}
if (layoutManager.canScrollVertically()) {
out[1] = mLayoutDirectionHelper.getScrollToAlignView(targetView);
}
if (mSnapBlockCallback != null) {
if (out[0] == 0 && out[1] == 0) {
mSnapBlockCallback.onBlockSnapped(layoutManager.getPosition(targetView));
} else {
mSnapBlockCallback.onBlockSnap(layoutManager.getPosition(targetView));
}
}
return out;
}
// We are flinging and need to know where we are heading.
@Override
public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager,
int velocityX, int velocityY) {
LinearLayoutManager lm = (LinearLayoutManager) layoutManager;
initItemDimensionIfNeeded(layoutManager);
mScroller.fling(0, 0, velocityX, velocityY, Integer.MIN_VALUE, Integer.MAX_VALUE,
Integer.MIN_VALUE, Integer.MAX_VALUE);
if (velocityX != 0) {
return mLayoutDirectionHelper
.getPositionsToMove(lm, mScroller.getFinalX(), mItemDimension);
}
if (velocityY != 0) {
return mLayoutDirectionHelper
.getPositionsToMove(lm, mScroller.getFinalY(), mItemDimension);
}
return RecyclerView.NO_POSITION;
}
// We have scrolled to the neighborhood where we will snap. Determine the snap position.
@Override
public View findSnapView(RecyclerView.LayoutManager layoutManager) {
// Snap to a view that is either 1) toward the bottom of the data and therefore on screen,
// or, 2) toward the top of the data and may be off-screen.
int snapPos = calcTargetPosition((LinearLayoutManager) layoutManager);
View snapView = (snapPos == RecyclerView.NO_POSITION)
? null : layoutManager.findViewByPosition(snapPos);
if (snapView == null) {
Log.d(TAG, "<<<<findSnapView is returning null!");
}
Log.d(TAG, "<<<<findSnapView snapos=" + snapPos);
return snapView;
}
// Does the heavy lifting for findSnapView.
private int calcTargetPosition(LinearLayoutManager layoutManager) {
int snapPos;
int firstVisiblePos = layoutManager.findFirstVisibleItemPosition();
if (firstVisiblePos == RecyclerView.NO_POSITION) {
return RecyclerView.NO_POSITION;
}
initItemDimensionIfNeeded(layoutManager);
if (firstVisiblePos >= mPriorFirstPosition) {
// Scrolling toward bottom of data
int firstCompletePosition = layoutManager.findFirstCompletelyVisibleItemPosition();
if (firstCompletePosition != RecyclerView.NO_POSITION
&& firstCompletePosition % mBlocksize == 0) {
snapPos = firstCompletePosition;
} else {
snapPos = roundDownToBlockSize(firstVisiblePos + mBlocksize);
}
} else {
// Scrolling toward top of data
snapPos = roundDownToBlockSize(firstVisiblePos);
// Check to see if target view exists. If it doesn't, force a smooth scroll.
// SnapHelper only snaps to existing views and will not scroll to a non-existant one.
// If limiting fling to single block, then the following is not needed since the
// views are likely to be in the RecyclerView pool.
if (layoutManager.findViewByPosition(snapPos) == null) {
int[] toScroll = mLayoutDirectionHelper.calculateDistanceToScroll(layoutManager, snapPos);
mRecyclerView.smoothScrollBy(toScroll[0], toScroll[1], sInterpolator);
}
}
mPriorFirstPosition = firstVisiblePos;
return snapPos;
}
private void initItemDimensionIfNeeded(final RecyclerView.LayoutManager layoutManager) {
if (mItemDimension != 0) {
return;
}
View child;
if ((child = layoutManager.getChildAt(0)) == null) {
return;
}
if (layoutManager.canScrollHorizontally()) {
mItemDimension = child.getWidth();
mBlocksize = getSpanCount(layoutManager) * (mRecyclerView.getWidth() / mItemDimension);
} else if (layoutManager.canScrollVertically()) {
mItemDimension = child.getHeight();
mBlocksize = getSpanCount(layoutManager) * (mRecyclerView.getHeight() / mItemDimension);
}
mMaxPositionsToMove = mBlocksize * mMaxFlingBlocks;
}
private int getSpanCount(RecyclerView.LayoutManager layoutManager) {
return (layoutManager instanceof GridLayoutManager)
? ((GridLayoutManager) layoutManager).getSpanCount()
: 1;
}
private int roundDownToBlockSize(int trialPosition) {
return trialPosition - trialPosition % mBlocksize;
}
private int roundUpToBlockSize(int trialPosition) {
return roundDownToBlockSize(trialPosition + mBlocksize - 1);
}
@Nullable
protected LinearSmoothScroller createScroller(RecyclerView.LayoutManager layoutManager) {
if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
return null;
}
return new LinearSmoothScroller(mRecyclerView.getContext()) {
@Override
protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
int[] snapDistances = calculateDistanceToFinalSnap(mRecyclerView.getLayoutManager(),
targetView);
final int dx = snapDistances[0];
final int dy = snapDistances[1];
final int time = calculateTimeForDeceleration(Math.max(Math.abs(dx), Math.abs(dy)));
if (time > 0) {
action.update(dx, dy, time, sInterpolator);
}
}
@Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
}
};
}
public void setSnapBlockCallback(@Nullable SnapBlockCallback callback) {
mSnapBlockCallback = callback;
}
/*
Helper class that handles calculations for LTR and RTL layouts.
*/
private class LayoutDirectionHelper {
// Is the layout an RTL one?
private final boolean mIsRTL;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
LayoutDirectionHelper(int direction) {
mIsRTL = direction == View.LAYOUT_DIRECTION_RTL;
}
/*
Calculate the amount of scroll needed to align the target view with the layout edge.
*/
int getScrollToAlignView(View targetView) {
return (mIsRTL)
? mOrientationHelper.getDecoratedEnd(targetView) - mRecyclerView.getWidth()
: mOrientationHelper.getDecoratedStart(targetView);
}
/**
* Calculate the distance to final snap position when the view corresponding to the snap
* position is not currently available.
*
* @param layoutManager LinearLayoutManager or descendent class
* @param targetPos - Adapter position to snap to
* @return int[2] {x-distance in pixels, y-distance in pixels}
*/
int[] calculateDistanceToScroll(LinearLayoutManager layoutManager, int targetPos) {
int[] out = new int[2];
int firstVisiblePos;
firstVisiblePos = layoutManager.findFirstVisibleItemPosition();
if (layoutManager.canScrollHorizontally()) {
if (targetPos <= firstVisiblePos) { // scrolling toward top of data
if (mIsRTL) {
View lastView = layoutManager.findViewByPosition(layoutManager.findLastVisibleItemPosition());
out[0] = mOrientationHelper.getDecoratedEnd(lastView)
+ (firstVisiblePos - targetPos) * mItemDimension;
} else {
View firstView = layoutManager.findViewByPosition(firstVisiblePos);
out[0] = mOrientationHelper.getDecoratedStart(firstView)
- (firstVisiblePos - targetPos) * mItemDimension;
}
}
}
if (layoutManager.canScrollVertically()) {
if (targetPos <= firstVisiblePos) { // scrolling toward top of data
View firstView = layoutManager.findViewByPosition(firstVisiblePos);
out[1] = firstView.getTop() - (firstVisiblePos - targetPos) * mItemDimension;
}
}
return out;
}
/*
Calculate the number of positions to move in the RecyclerView given a scroll amount
and the size of the items to be scrolled. Return integral multiple of mBlockSize not
equal to zero.
*/
int getPositionsToMove(LinearLayoutManager llm, int scroll, int itemSize) {
int positionsToMove;
positionsToMove = roundUpToBlockSize(Math.abs(scroll) / itemSize);
if (positionsToMove < mBlocksize) {
// Must move at least one block
positionsToMove = mBlocksize;
} else if (positionsToMove > mMaxPositionsToMove) {
// Clamp number of positions to move so we don't get wild flinging.
positionsToMove = mMaxPositionsToMove;
}
if (scroll < 0) {
positionsToMove *= -1;
}
if (mIsRTL) {
positionsToMove *= -1;
}
if (mLayoutDirectionHelper.isDirectionToBottom(scroll < 0)) {
// Scrolling toward the bottom of data.
return roundDownToBlockSize(llm.findFirstVisibleItemPosition()) + positionsToMove;
}
// Scrolling toward the top of the data.
return roundDownToBlockSize(llm.findLastVisibleItemPosition()) + positionsToMove;
}
boolean isDirectionToBottom(boolean velocityNegative) {
//noinspection SimplifiableConditionalExpression
return mIsRTL ? velocityNegative : !velocityNegative;
}
}
public interface SnapBlockCallback {
void onBlockSnap(int snapPosition);
void onBlockSnapped(int snapPosition);
}
private static final float MILLISECONDS_PER_INCH = 100f;
@SuppressWarnings("unused")
private static final String TAG = "SnapToBlock";
}
-----------------------
<com.takusemba.multisnaprecyclerview.MultiSnapRecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:msrv_gravity="start"
app:msrv_snap_count="3" />
How to get soon-to-be-selected page while settling snapping feature on RecyclerView and ViewPager
val inflater = LayoutInflater.from(this)
val viewPagerHolders = HashMap<Int, ViewPagerViewHolder>()
viewPager.adapter = object : RecyclerPagerAdapter<ViewPagerViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewPagerViewHolder {
return ViewPagerViewHolder(inflater.inflate(R.layout.cell, parent, false))
}
override fun getItemCount(): Int = 100
override fun onBindViewHolder(holder: ViewPagerViewHolder, position: Int) {
holder.textView.text = position.toString()
viewPagerHolders.remove(holder.adapterPosition)
holder.adapterPosition = position
viewPagerHolders[position] = holder
}
}
viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
var selectedHolder: ViewPagerViewHolder? = null
override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) {
val holder = viewPagerHolders[position]
if (holder != null) {
if (selectedHolder != null && selectedHolder != holder)
selectedHolder!!.textView.text = selectedHolder!!.adapterPosition.toString()
holder.textView.text = "selected:${position.toString()}"
selectedHolder = holder
}
}
})
class ViewPagerViewHolder(itemView: View) : RecyclerPagerAdapter.ViewHolder(itemView) {
val textView: TextView = itemView.findViewById(android.R.id.text1)
var adapterPosition: Int = Int.MIN_VALUE
}
-----------------------
val inflater = LayoutInflater.from(this)
val viewPagerHolders = HashMap<Int, ViewPagerViewHolder>()
viewPager.adapter = object : RecyclerPagerAdapter<ViewPagerViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewPagerViewHolder {
return ViewPagerViewHolder(inflater.inflate(R.layout.cell, parent, false))
}
override fun getItemCount(): Int = 100
override fun onBindViewHolder(holder: ViewPagerViewHolder, position: Int) {
holder.textView.text = position.toString()
viewPagerHolders.remove(holder.adapterPosition)
holder.adapterPosition = position
viewPagerHolders[position] = holder
}
}
viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
var selectedHolder: ViewPagerViewHolder? = null
override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) {
val holder = viewPagerHolders[position]
if (holder != null) {
if (selectedHolder != null && selectedHolder != holder)
selectedHolder!!.textView.text = selectedHolder!!.adapterPosition.toString()
holder.textView.text = "selected:${position.toString()}"
selectedHolder = holder
}
}
})
class ViewPagerViewHolder(itemView: View) : RecyclerPagerAdapter.ViewHolder(itemView) {
val textView: TextView = itemView.findViewById(android.R.id.text1)
var adapterPosition: Int = Int.MIN_VALUE
}
Adding Youtube API in application using Firebase in Android
youTubePlayerView.initialize("YOUR API KEY",
new YouTubePlayer.OnInitializedListener() {
@Override
public void onInitializationSuccess(YouTubePlayer.Provider provider,
YouTubePlayer youTubePlayer, boolean b) {
youTubePlayer.loadVideo("VIDEO_ID_HERE");
}
@Override
public void onInitializationFailure(YouTubePlayer.Provider provider,
YouTubeInitializationResult youTubeInitializationResult) {
}
});
how to add fragment in recycler adapter?
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
public RecyclerViewConstructor(Activity hostActivity) {
super();
this.hostActivity = hostActivity;
lastClickTime = 0;
}
Activity host
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View rootView = LayoutInflater.from(mContext).inflate(R.layout.item_fragment, null);
return new ViewHolder(rootView);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
YourFragment yourFragment = YourFragment.newInstance();
FragmentManager fm = hostActivity.getSupportFragmentManager();
fm.addOnBackStackChangedListener(this);
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.fragment_container, fragment, tag);
}
-----------------------
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
public RecyclerViewConstructor(Activity hostActivity) {
super();
this.hostActivity = hostActivity;
lastClickTime = 0;
}
Activity host
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View rootView = LayoutInflater.from(mContext).inflate(R.layout.item_fragment, null);
return new ViewHolder(rootView);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
YourFragment yourFragment = YourFragment.newInstance();
FragmentManager fm = hostActivity.getSupportFragmentManager();
fm.addOnBackStackChangedListener(this);
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.fragment_container, fragment, tag);
}
-----------------------
public class ViewHolderInds extends RecyclerView.ViewHolder {
...
public final int frameId; //Unique identifier for ValueFrame
...
public ViewHolderInds(View view) {
super(view);
...
frameId = view.generateViewId();
((FrameLayout) view.findViewById(R.id.valueFrame)).setId(frameId);
...
}
}
@Override
public void onBindViewHolder(@NonNull ViewHolderInds holder, int position) {
getActivity().getSupportFragmentManager().
beginTransaction().
replace(holder.frameId, EditFragment.newInstance(...)).
commit();
...
-----------------------
public class ViewHolderInds extends RecyclerView.ViewHolder {
...
public final int frameId; //Unique identifier for ValueFrame
...
public ViewHolderInds(View view) {
super(view);
...
frameId = view.generateViewId();
((FrameLayout) view.findViewById(R.id.valueFrame)).setId(frameId);
...
}
}
@Override
public void onBindViewHolder(@NonNull ViewHolderInds holder, int position) {
getActivity().getSupportFragmentManager().
beginTransaction().
replace(holder.frameId, EditFragment.newInstance(...)).
commit();
...
Android build gradle fail on specific device
compile 'com.squareup.okhttp3:okhttp:3.8.1'
compile 'com.squareup.picasso:picasso:2.5.2'
android {
configurations{
all*.exclude module: 'okhttp'
all*.exclude module: 'okio'
}
}
-----------------------
compile 'com.squareup.okhttp3:okhttp:3.8.1'
compile 'com.squareup.picasso:picasso:2.5.2'
android {
configurations{
all*.exclude module: 'okhttp'
all*.exclude module: 'okio'
}
}
-----------------------
compile 'com.squareup.okhttp:okhttp:2.4.0'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.2.0'
Android Build Tools 3.1.1 failed build finished with non-zero exit value 1
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
ConstraintLayout inside DrawerLayout causes some problems
<ScrollView
android:id="@+id/scrollViewDashboard"
android:layout_width="match_parent"
android:layout_height="match_parent">
Android, AsyncTask not calling onProgressUpdate and onPostExecute
new GetPlaylistAsyncTask(mYouTubeDataApi,getContext()) {
@Override
public void onPostExecute(Pair<String, List<Video>> result) {
handleGetPlaylistResult(playlistVideos, result);
}
}.execute(playlistVideos.playlistId, playlistVideos.getNextPageToken());
new GetPlaylistAsyncTask(mYouTubeDataApi,getContext(),YouTubeRecyclerViewFragment.this)
.execute(playlistVideos.playlistId, playlistVideos.getNextPageToken());
@Override
public void handleGetPlaylistResult(PlaylistVideos playlistVideos, Pair<String, List<Video>> result) {
if (result == null) return;
if (playlistVideos == null){
playlistVideos = mPlaylistVideos;
}
final int positionStart = playlistVideos.size();
playlistVideos.setNextPageToken(result.first);
playlistVideos.addAll(result.second);
mPlaylistCardAdapter.notifyItemRangeInserted(positionStart, result.second.size());
}
public class GetPlaylistAsyncTask{
private AsyncResponse theListener;
public GetPlaylistAsyncTask(YouTube api, Context context, YouTubeRecyclerViewFragment frag ) {
mYouTubeDataApi = api;
mContext = context;
theListener = (AsyncResponse)frag;
}
@Override
public void onPostExecute(Pair<String, List<Video>> result) {
progressBar.dismiss();
theListener.handleGetPlaylistResult(null,result);
}
}
-----------------------
new GetPlaylistAsyncTask(mYouTubeDataApi,getContext()) {
@Override
public void onPostExecute(Pair<String, List<Video>> result) {
handleGetPlaylistResult(playlistVideos, result);
}
}.execute(playlistVideos.playlistId, playlistVideos.getNextPageToken());
new GetPlaylistAsyncTask(mYouTubeDataApi,getContext(),YouTubeRecyclerViewFragment.this)
.execute(playlistVideos.playlistId, playlistVideos.getNextPageToken());
@Override
public void handleGetPlaylistResult(PlaylistVideos playlistVideos, Pair<String, List<Video>> result) {
if (result == null) return;
if (playlistVideos == null){
playlistVideos = mPlaylistVideos;
}
final int positionStart = playlistVideos.size();
playlistVideos.setNextPageToken(result.first);
playlistVideos.addAll(result.second);
mPlaylistCardAdapter.notifyItemRangeInserted(positionStart, result.second.size());
}
public class GetPlaylistAsyncTask{
private AsyncResponse theListener;
public GetPlaylistAsyncTask(YouTube api, Context context, YouTubeRecyclerViewFragment frag ) {
mYouTubeDataApi = api;
mContext = context;
theListener = (AsyncResponse)frag;
}
@Override
public void onPostExecute(Pair<String, List<Video>> result) {
progressBar.dismiss();
theListener.handleGetPlaylistResult(null,result);
}
}
-----------------------
new GetPlaylistAsyncTask(mYouTubeDataApi,getContext()) {
@Override
public void onPostExecute(Pair<String, List<Video>> result) {
handleGetPlaylistResult(playlistVideos, result);
}
}.execute(playlistVideos.playlistId, playlistVideos.getNextPageToken());
new GetPlaylistAsyncTask(mYouTubeDataApi,getContext(),YouTubeRecyclerViewFragment.this)
.execute(playlistVideos.playlistId, playlistVideos.getNextPageToken());
@Override
public void handleGetPlaylistResult(PlaylistVideos playlistVideos, Pair<String, List<Video>> result) {
if (result == null) return;
if (playlistVideos == null){
playlistVideos = mPlaylistVideos;
}
final int positionStart = playlistVideos.size();
playlistVideos.setNextPageToken(result.first);
playlistVideos.addAll(result.second);
mPlaylistCardAdapter.notifyItemRangeInserted(positionStart, result.second.size());
}
public class GetPlaylistAsyncTask{
private AsyncResponse theListener;
public GetPlaylistAsyncTask(YouTube api, Context context, YouTubeRecyclerViewFragment frag ) {
mYouTubeDataApi = api;
mContext = context;
theListener = (AsyncResponse)frag;
}
@Override
public void onPostExecute(Pair<String, List<Video>> result) {
progressBar.dismiss();
theListener.handleGetPlaylistResult(null,result);
}
}
QUESTION
Could not find multidex.jar (com.android.support:multidex:1.0.2)
Asked 2020-May-21 at 11:15Could not find multidex.jar (com.android.support:multidex:1.0.2). Searched in the following locations: https://jcenter.bintray.com/com/android/support/multidex/1.0.2/multidex
I have just installed latest version of Android Studio 3.1.3 When i tried to sync my project it shows failed to resolve : multidex
Also i tried https://jcenter.bintray.com/com/android/support/multidex/1.0.2/multidex this link which shows 404 error. Please help
Project Level :
// Top-level build file where you can add configuration options common to all sub-projects/modules.
apply from: 'buildsystem/dependencies.gradle'
buildscript {
repositories {
jcenter()
mavenCentral()
maven { url 'https://maven.fabric.io/public' }
maven {
url 'https://maven.google.com'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath 'com.frogermcs.androiddevmetrics:androiddevmetrics-plugin:0.5'
classpath 'com.google.gms:google-services:3.1.2'
classpath 'io.fabric.tools:gradle:1.25.1'
}
}
allprojects {
ext {
androidApplicationId = 'com.medikoe.connect'
androidVersionCode = 23
androidVersionName = "1.7.2"
testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
testApplicationId = 'com.medikoe.test'
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
repositories {
google()
}
App Level :
apply plugin: 'com.android.application'
apply plugin: 'io.fabric'
apply plugin: 'com.frogermcs.androiddevmetrics'
configurations {
devDebugCompile
devSignedDebugCompile
devPreReleaseCompile
devReleaseCompile
prodDebugCompile
prodSignedDebugCompile
prodPreReleaseCompile
prodReleaseCompile
}
android {
def globalConfiguration = rootProject.extensions.getByName("ext")
compileSdkVersion globalConfiguration.getAt("androidCompileSdkVersion")
buildToolsVersion globalConfiguration.getAt("androidBuildToolsVersion")
defaultConfig {
minSdkVersion globalConfiguration.getAt("androidMinSdkVersion")
targetSdkVersion globalConfiguration.getAt("androidTargetSdkVersion")
versionCode globalConfiguration.getAt("androidVersionCode")
applicationId globalConfiguration.getAt("androidApplicationId")
testInstrumentationRunner globalConfiguration.getAt("testInstrumentationRunner")
testApplicationId globalConfiguration.getAt("testApplicationId")
multiDexEnabled true
vectorDrawables.useSupportLibrary = true
}
dexOptions {
jumboMode true
javaMaxHeapSize "6g"
}
compileOptions {
targetCompatibility 1.8
sourceCompatibility 1.8
}
packagingOptions {
exclude 'LICENSE.txt'
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/ASL2.0'
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
}
lintOptions {
quiet true
abortOnError false
ignoreWarnings true
disable 'InvalidPackage' //Some libraries have issues with this.
disable 'OldTargetApi'
//Lint gives this warning but SDK 20 would be Android L Beta.
disable 'IconDensities' //For testing purpose. This is safe to remove.
disable 'IconMissingDensityFolder' //For testing purpose. This is safe to remove.
}
signingConfigs {
debug {
storeFile file('../buildsystem/debug.keystore')
storePassword 'android'
keyAlias '********'
keyPassword '******'
}
signedDebug {
storeFile file('../buildsystem/keystore_medikoe.jks')
storePassword '*********'
keyAlias '*********'
keyPassword '**********'
}
preRelease {
storeFile file('../buildsystem/keystore_medikoe.jks')
storePassword '**********'
keyAlias '******'
keyPassword '********'
}
release {
storeFile file('../buildsystem/keystore_medikoe.jks')
storePassword '************'
keyAlias '**********'
keyPassword '************'
}
}
dataBinding {
enabled = false
}
flavorDimensions "default"
productFlavors {
dev {
// Enable pre-dexing to produce an APK that can be tested on
// Android 5.0+ without the time-consuming DEX build processes.
minSdkVersion 19
}
prod {
// The actual minSdkVersion for the production version.
minSdkVersion globalConfiguration.getAt("androidMinSdkVersion")
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
applicationIdSuffix ".debug"
resValue "string", "app_name", "Connect-D"
buildConfigField "boolean", "PRE_RELEASE", "false"
}
signedDebug {
minifyEnabled true
zipAlignEnabled true
shrinkResources true
debuggable true
signingConfig signingConfigs.signedDebug
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
applicationIdSuffix ".signeddebug"
resValue "string", "app_name", "Connect-SD"
buildConfigField "boolean", "PRE_RELEASE", "false"
}
preRelease {
minifyEnabled true
zipAlignEnabled true
shrinkResources true
debuggable false
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
applicationIdSuffix ".prerelease"
resValue "string", "app_name", "Connect-PR"
buildConfigField "boolean", "PRE_RELEASE", "true"
}
release {
minifyEnabled true
zipAlignEnabled true
shrinkResources true
debuggable false
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
resValue "string", "app_name", "Medikoe Connect"
buildConfigField "boolean", "PRE_RELEASE", "false"
}
}
}
dependencies {
def presentationDependencies = rootProject.ext.presentationDependencies
def presentationTestDependencies = rootProject.ext.presentationTestDependencies
def developmentDependencies = rootProject.ext.developmentDependencies
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
implementation 'com.android.support:support-v4:27+'
compile project(':Domain')
devDebugCompile project(path: ':Data')
devSignedDebugCompile project(path: ':Data')
devPreReleaseCompile project(path: ':Data')
devReleaseCompile project(path: ':Data')
prodDebugCompile project(path: ':Data')
prodSignedDebugCompile project(path: ':Data')
prodPreReleaseCompile project(path: ':Data')
prodReleaseCompile project(path: ':Data')
devDebugCompile project(path: ':Infrastructure')
devSignedDebugCompile project(path: ':Infrastructure')
devPreReleaseCompile project(path: ':Infrastructure')
devReleaseCompile project(path: ':Infrastructure')
prodDebugCompile project(path: ':Infrastructure')
prodSignedDebugCompile project(path: ':Infrastructure')
prodPreReleaseCompile project(path: ':Infrastructure')
prodReleaseCompile project(path: ':Infrastructure')
//compile files('libs/date4j.jar')
//compile files('libs/commons-collections4-4.1.jar')
annotationProcessor presentationDependencies.daggerCompiler
compile presentationDependencies.dagger
compile presentationDependencies.rxJava
compile presentationDependencies.rxAndroid
provided presentationDependencies.javaxAnnotation
//compile presentationDependencies.autoFactory
compile presentationDependencies.butterKnife
annotationProcessor presentationDependencies.butterKnifeCompiler
compile presentationDependencies.designLibrary
compile presentationDependencies.recyclerView
compile presentationDependencies.cardView
compile presentationDependencies.percent
compile presentationDependencies.glide
compile presentationDependencies.glideOkhttp3
compile presentationDependencies.nineOldAndroids
//compile presentationDependencies.ahBottomNav
//compile presentationDependencies.lapismSearch
//compile presentationDependencies.FragmentNavigator
compile presentationDependencies.flexibleDivider
compile presentationDependencies.materialDialogsCore
compile presentationDependencies.materialFavoriteButton
compile presentationDependencies.fancyButtons
compile presentationDependencies.simpleRatingBar
compile presentationDependencies.circularImageView
compile presentationDependencies.waveLoadingView
compile presentationDependencies.googleAutoFactory
annotationProcessor presentationDependencies.googleAutoFactory
compile presentationDependencies.playServicesPlaces
compile presentationDependencies.playServicesLocation
//compile presentationDependencies.tagGroup
//compile presentationDependencies.expandableTextView
//compile presentationDependencies.expandableLayout
//compile presentationDependencies.touchImageView
//compile presentationDependencies.sublimePicker
compile presentationDependencies.rxpermissions
compile presentationDependencies.rxActivityResult
compile presentationDependencies.rxBinding
compile presentationDependencies.rxPaparazzo
compile presentationDependencies.rxBinding
compile presentationDependencies.parcelerApi
annotationProcessor presentationDependencies.parceler
compile presentationDependencies.guava
compile presentationDependencies.weekView
compile presentationDependencies.jodaTime
compile presentationDependencies.rxPaparazzo
//compile presentationDependencies.collection
compile presentationDependencies.RecyclerViewPager
//compile presentationDependencies.googleAnalytics
compile presentationDependencies.SpotsProgressDialog
compile presentationDependencies.CustomActivityOnCrash
compile presentationDependencies.multiDex
compile presentationDependencies.SaripaarVersion
//compile presentationDependencies.RazorPay
compile presentationDependencies.Timecon
compile presentationDependencies.ClansFab
compile presentationDependencies.MPAndroidChart
compile presentationDependencies.FragNav
compile presentationDependencies.CircularFillableLoaders
compile presentationDependencies.AvatarImageView
compile presentationDependencies.AVLoadingIndicatorView
compile presentationDependencies.Epoxy
annotationProcessor presentationDependencies.EpoxyProcessor
compile presentationDependencies.RelativeTimeTextView
//compile presentationDependencies.AudioPlayerView
compile presentationDependencies.EasyVideoPlayer
compile presentationDependencies.ThreeTenAndroid
//compile presentationDependencies.Steppers
//compile presentationDependencies.RxAccountManager
compile presentationDependencies.MaterialProgressBar
compile presentationDependencies.FloatingView
// compile presentationDependencies.HashtagView{
// //Guava appears to be included explicitly in the list-services.jar
// //Skip the redundant dependency
// exclude group: 'com.google.guava'
// }
compile('com.github.greenfrvr:hashtag-view:1.3.1@aar') {
transitive = false
}
androidTestCompile presentationTestDependencies.mockito
androidTestCompile presentationTestDependencies.dexmaker
androidTestCompile presentationTestDependencies.dexmakerMockito
androidTestCompile presentationTestDependencies.espresso
androidTestCompile presentationTestDependencies.testingSupportLib
androidTestCompile presentationTestDependencies.androidAnnotations
androidTestCompile presentationTestDependencies.autoFactory
// androidTestCompile presentationTestDependencies.guava
//Development
compile developmentDependencies.leakCanary
compile developmentDependencies.DebugDatabase
compile 'com.facebook.stetho:stetho:1.4.1'
compile 'com.facebook.stetho:stetho-okhttp3:1.4.1'
compile('com.razorpay:checkout:1.4.4')
compile 'com.aurelhubert:ahbottomnavigation:2.1.0'
compile 'com.github.florent37:materialtextfield:1.0.7'
compile 'com.github.rahatarmanahmed:circularprogressview:2.5.0'
compile('com.github.ganfra:material-spinner:2.0.0')
compile 'com.wdullaer:materialdatetimepicker:3.5.0'
compile 'com.myhexaville:smart-image-picker:1.0'
compile 'com.github.blikoon:QRCodeScanner:0.1.2'
compile 'de.hdodenhof:circleimageview:2.2.0'
compile 'com.github.IntruderShanky:Frisson:2.1.1'
compile 'com.jsibbold:zoomage:1.1.0'
compile 'com.github.emrekose26:PinchZoom:-SNAPSHOT'
compile 'com.ablanco.zoomy:zoomy:1.0.0'
compile 'xyz.danoz:recyclerviewfastscroller:0.1.3'
compile 'com.irozon.sneaker:sneaker:1.0.2'
compile 'com.kyanogen.signatureview:signature-view:1.2'
compile 'com.getkeepsafe.taptargetview:taptargetview:1.11.0'
compile 'com.rengwuxian.materialedittext:library:2.1.4'
implementation 'com.github.takusemba:multisnaprecyclerview:1.3.3'
compile 'com.github.chahine:pageindicator:0.2.6'
compile 'com.github.florent37:tutoshowcase:1.0.1'
implementation 'io.supercharge:shimmerlayout:2.1.0'
compile 'com.github.yeriomin:play-store-api:0.19'
compile 'com.squareup.okhttp3:okhttp:3.8.1'
implementation 'com.github.antonKozyriatskyi:CircularProgressIndicator:1.0.5'}
configurations.all {
exclude group: 'com.android.support', module: 'support-v13'
}
apply plugin: 'com.google.gms.google-services'
ANSWER
Answered 2018-Jun-11 at 07:07Since you are developing using Android Studio 3.1.3, try to update your build.gradle
dependencies and plugin as well to their latest version.
And move your repository google()
to the buildscript
.
You can try this:
Top-level build.gradle
apply from: 'buildsystem/dependencies.gradle'
buildscript {
repositories {
jcenter()
mavenCentral()
maven { url 'https://maven.fabric.io/public' }
google()
//maven {
// url 'https://maven.google.com'
//}
}
dependencies {
//Update it to the latest version: 3.1.3
classpath 'com.android.tools.build:gradle:3.1.3'
classpath 'com.frogermcs.androiddevmetrics:androiddevmetrics-plugin:0.5'
classpath 'com.google.gms:google-services:3.1.2'
classpath 'io.fabric.tools:gradle:1.25.1'
}
}
allprojects {
ext {
androidApplicationId = 'com.medikoe.connect'
androidVersionCode = 23
androidVersionName = "1.7.2"
testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
testApplicationId = 'com.medikoe.test'
}
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
// Remove these lines
// repositories {
// google()
// }
Some other tips:
For your app build.gradle
, try to change compile
to implementation
, since it will be deprecated soon.
Community Discussions, Code Snippets contain sources that include Stack Exchange Network
No vulnerabilities reported
Save this library and start creating your kit
Explore Related Topics
Save this library and start creating your kit