Hire the author: Dennis M
Pull Layout Android Image Source unsplash.com.
Here is the GitHub link for this project double-pull-android.
Introduction
In this article, you will learn how to implement quickly and simply a working double pull Layout feature in your app using the Scroll Adapter in Android. The first section of this project involves scrolling down the screen to display what is below the partial header. The second part involves scrolling up the screen so that the partial header disappears and the whole screen becomes visible. Therefore, it’s a double layout since you will be able to pull the screen both up and down enabling you to view both layouts simultaneously.
This project provides insight into the functionality of dual drag editing on android. In addition, the user gains a better understanding of the internal machinery that runs after using those features. This article expects you to have some experience in building Android apps in Kotlin
What motivates me to do this work is “How to create a double pull layout in Android studio”? I have created an Android app that uses the dual drag design required by users to connect to the system. In conclusion, this blog post will provide you with direct clues on how to use dual drag architecture on Android.
Photo Preview
Glossary
ScrollView is a viewing group that allows the management category placed within it to be scripted. Scroll views can have one straight child embedded in it. To add more views within a scroll view, create a direct child that adds a view group, for example, LinearLayout, and then add additional views within that Line. Scroll view only supports vertical scrolling. To scroll horizontally, use HorizontalScrollView instead. Never add RecyclerView or ListView to the scrolling view. Doing so causes malfunctioning of the user interface and poor user experience.
RecyclerView makes it easy to accurately display large data sets. You submit information and explain what each item looks like, and the RecyclerView library does things as fast as it can. As the name implies, RecyclerView updates those individual items. When an object scratches off the screen, RecyclerView does not damage its view. Instead, RecyclerView uses the new scratch-screen viewing. This reuse improves performance, improves the performance of your app, and reduces power consumption.
Frame Layout is a viewing group that allows a segment of viewers placed within it to be scanned. Scroll views can have one straight child embedded in it. To add multiple views within a scroll view, create a direct child that adds a view group, for example, LinearLayout, and add additional views to that LinearLayout
Steps:
Step 1: Adding required dependencies
Firstly, go to the app-level build.gradle file and add the following dependency:
dependencies { | |
implementation fileTree(include: ['*.jar'], dir: 'libs') | |
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${versions.kotlin}" | |
implementation "androidx.constraintlayout:constraintlayout:${versions.constraintLayout}" | |
implementation "androidx.appcompat:appcompat:${versions.supportLibrary}" | |
implementation "com.google.android.material:material:${versions.supportLibrary}" | |
implementation project(':double-pull-delegate') | |
} |
Secondly, the build.gradle file will be as shown below:
apply plugin: 'com.android.application' | |
apply plugin: 'kotlin-android' | |
apply plugin: 'kotlin-android-extensions' | |
apply plugin: 'kotlin-kapt' | |
android { | |
compileSdkVersion versions.compileSdk | |
defaultConfig { | |
applicationId "com.dennis.doublepull" | |
minSdkVersion versions.minSdk | |
targetSdkVersion versions.targetSdk | |
versionCode 1 | |
versionName "1.0" | |
} | |
buildTypes { | |
release { | |
minifyEnabled false | |
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | |
} | |
} | |
compileOptions { | |
sourceCompatibility JavaVersion.VERSION_1_8 | |
targetCompatibility JavaVersion.VERSION_1_8 | |
} | |
} | |
dependencies { | |
implementation fileTree(include: ['*.jar'], dir: 'libs') | |
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${versions.kotlin}" | |
implementation "androidx.constraintlayout:constraintlayout:${versions.constraintLayout}" | |
implementation "androidx.appcompat:appcompat:${versions.supportLibrary}" | |
implementation "com.google.android.material:material:${versions.supportLibrary}" | |
implementation project(':double-pull-delegate') | |
} |
Step 2: Creating the activity_main.xml file
Now, come to your activity_main.xml file which is responsible for designing the layout of the app and it’s located in the layout folder under the res folder. Change your default to Relative Layout then add additional attributes like this:
<?xml version="1.0" encoding="utf-8"?> | |
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent"> | |
<include layout="@layout/main_in"/> | |
<include layout="@layout/main_out"/> | |
</RelativeLayout> |
As shown above, the activity_main.xml is made up of two layouts which are main_in.xml and main_out.xml. Go to the layout folder in the res folder and create a new layout called main_in.xml and add the following code as shown below.
<?xml version="1.0" encoding="utf-8"?> | |
<merge xmlns:android="http://schemas.android.com/apk/res/android" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent"> | |
<androidx.recyclerview.widget.RecyclerView | |
android:id="@+id/main_rcv" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" /> | |
<RelativeLayout | |
android:layout_width="match_parent" | |
android:layout_height="45dp" | |
android:layout_alignParentBottom="true" | |
android:background="#aa000000" | |
android:gravity="center"> | |
<ImageButton | |
android:id="@+id/main_open_iv" | |
android:background="@null" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:padding="15dp" | |
android:src="@drawable/open" /> | |
</RelativeLayout> | |
</merge |
Create another layout called main_out.xml and add the following code as shown below.
<?xml version="1.0" encoding="utf-8"?> | |
<dennis.pull.widget.PullScrollView xmlns:android="http://schemas.android.com/apk/res/android" | |
android:id="@+id/main_root" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent"> | |
<RelativeLayout | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content"> | |
<dennis.pull.widget.HeaderRelativeLayout | |
android:id="@+id/main_header" | |
android:layout_width="match_parent" | |
android:layout_height="275dp"> | |
<ImageView | |
android:id="@+id/main_header_icon" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:scaleType="centerCrop" | |
android:src="@drawable/header2" /> | |
<ImageView | |
android:id="@+id/main_header_mask" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:scaleType="fitXY" | |
android:src="@drawable/bk_vediomask_bottom" /> | |
</dennis.pull.widget.HeaderRelativeLayout> | |
<dennis.pull.widget.BodyRelativeLayout | |
android:id="@+id/main_body" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:layout_marginTop="200dp"> | |
<RelativeLayout | |
android:id="@+id/main_content" | |
android:background="@android:color/white" | |
android:layout_width="match_parent" | |
android:layout_height="1000dp" | |
android:layout_marginTop="75dp"> | |
<TextView | |
android:textStyle="bold" | |
android:textColor="#bb000000" | |
android:textSize="20sp" | |
android:text="This is UP" | |
android:layout_marginTop="120dp" | |
android:layout_centerHorizontal="true" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" /> | |
<TextView | |
android:textStyle="bold" | |
android:textColor="#bb000000" | |
android:textSize="20sp" | |
android:text="This is CENTER" | |
android:layout_centerInParent="true" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" /> | |
<TextView | |
android:textStyle="bold" | |
android:textColor="#bb000000" | |
android:textSize="20sp" | |
android:text="This is BOTTOM" | |
android:layout_marginBottom="150dp" | |
android:layout_alignParentBottom="true" | |
android:layout_centerHorizontal="true" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" /> | |
</RelativeLayout> | |
<ImageView | |
android:id="@+id/main_haibao" | |
android:layout_width="100dp" | |
android:layout_height="150dp" | |
android:layout_marginLeft="15dp" | |
android:scaleType="centerCrop" | |
android:src="@drawable/haibao2" /> | |
<LinearLayout | |
android:id="@+id/main_info" | |
android:layout_width="wrap_content" | |
android:layout_height="150dp" | |
android:layout_toRightOf="@id/main_haibao" | |
android:orientation="vertical" | |
android:paddingLeft="20dp"> | |
<LinearLayout | |
android:layout_gravity="center_vertical" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:orientation="horizontal"> | |
<ImageView | |
android:layout_width="18dp" | |
android:layout_height="14dp" | |
android:scaleType="centerCrop" | |
android:src="@drawable/rating_bigger_select" /> | |
<ImageView | |
android:layout_width="18dp" | |
android:layout_height="14dp" | |
android:scaleType="centerCrop" | |
android:src="@drawable/rating_bigger_select" /> | |
<ImageView | |
android:layout_width="18dp" | |
android:layout_height="14dp" | |
android:scaleType="centerCrop" | |
android:src="@drawable/rating_bigger_select" /> | |
<ImageView | |
android:layout_width="18dp" | |
android:layout_height="14dp" | |
android:scaleType="centerCrop" | |
android:src="@drawable/rating_bigger_select" /> | |
<ImageView | |
android:layout_width="18dp" | |
android:layout_height="14dp" | |
android:scaleType="centerCrop" | |
android:src="@drawable/rating_bigger_select" /> | |
<TextView | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_marginLeft="10dp" | |
android:gravity="center" | |
android:text="9.9" | |
android:textColor="#ff5021" | |
android:textSize="15sp" | |
android:textStyle="bold" /> | |
</LinearLayout> | |
<TextView | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_marginTop="10dp" | |
android:text="A KENYAN FILM" | |
android:textColor="@android:color/white" | |
android:textSize="21sp" /> | |
</LinearLayout> | |
</dennis.pull.widget.BodyRelativeLayout> | |
</RelativeLayout> | |
</dennis.pull.widget.PullScrollView> |
Step 3: Creating the ScrollAdapter.kt file
ScrollAdapter will use the onCreateViewHolder which creates another viewer as no other RecyclerView holders can reuse it. This way, for example, if your RecyclerView can display 5 items at a time, it will create 5-6 ViewHolders, and then reuse them, each time you run to BindViewHolder.
In RecyclerView – you don’t need to compress such a large amount by reusing ViewHolders as you do with ListView. The con is, RecyclerView is truly customizable, however, it is not a complete help – it is not at all like ListView is not completely customizable, but it has great basic advantages.
package com.dennis.doublepull.adapter | |
import android.view.LayoutInflater | |
import android.view.View | |
import android.view.ViewGroup | |
import androidx.recyclerview.widget.RecyclerView | |
import com.dennis.doublepull.R | |
class ScrollAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() { | |
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { | |
return if (viewType == TYPE_HEADER) { | |
HeaderViewHolder(LayoutInflater.from(parent.context) | |
.inflate(R.layout.header, parent, false)) | |
} else ScrollViewHolder(LayoutInflater.from(parent.context) | |
.inflate(R.layout.item, parent, false)) | |
} | |
override fun getItemViewType(position: Int): Int { | |
return if (position == 0) { | |
TYPE_HEADER | |
} else TYPE_NORMAL | |
} | |
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { | |
// do nothing | |
} | |
override fun getItemCount(): Int { | |
return DEFAULT_SIZE | |
} | |
private inner class ScrollViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) | |
private inner class HeaderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) | |
companion object { | |
private const val TYPE_NORMAL = 1000 | |
private const val TYPE_HEADER = 2000 | |
private const val DEFAULT_SIZE = 31 | |
} | |
} |
Step 4: Creating the ScrollViewDelegate.kt file
You will use the setPullRelativeLayoutState function to make the scroll view work effectively as shown below
package dennis.pull.delegate | |
import android.view.MotionEvent | |
import android.view.View | |
import dennis.pull.ScrollState | |
import dennis.pull.listener.OnScrollChangedListener | |
class ScrollViewDelegate { | |
var scrollState = ScrollState.SHOW | |
private set | |
private var mOnScrollChangedListener: OnScrollChangedListener? = null | |
var downY: Int = 0 | |
private set | |
var moveY: Int = 0 | |
private set | |
fun setPullRelativeLayoutState(state: ScrollState) { | |
scrollState = state | |
} | |
fun onScrollChanged(view: View, l: Int, t: Int, oldl: Int, oldt: Int) { | |
if (mOnScrollChangedListener != null) { | |
mOnScrollChangedListener!!.onScrollChange(view, l, t, oldl, oldt) | |
} | |
} | |
fun onInterceptTouch(ev: MotionEvent) { | |
when (ev.action) { | |
MotionEvent.ACTION_DOWN -> downY = ev.rawY.toInt() | |
MotionEvent.ACTION_MOVE -> moveY = ev.rawY.toInt() | |
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> { | |
downY = 0 | |
moveY = 0 | |
} | |
} | |
} | |
fun setOnScrollChangedListener(listener: OnScrollChangedListener) { | |
mOnScrollChangedListener = listener | |
} | |
} |
Step 5: Creating the PullScrollView.kt file
The PullScrollView is only able to work by linking it to the Scrollview above and by using the several functions in place.
package dennis.pull.widget | |
import android.content.Context | |
import android.util.AttributeSet | |
import android.view.MotionEvent | |
import android.widget.ScrollView | |
import dennis.pull.ScrollState | |
import dennis.pull.delegate.ScrollViewDelegate | |
import dennis.pull.listener.OnScrollChangedListener | |
class PullScrollView : ScrollView { | |
private var mScrollViewDelegate: ScrollViewDelegate? = null | |
constructor(context: Context) : super(context) { | |
init() | |
} | |
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { | |
init() | |
} | |
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : | |
super(context, attrs, defStyleAttr) { | |
init() | |
} | |
private fun init() { | |
mScrollViewDelegate = ScrollViewDelegate() | |
} | |
fun setPullRelativeLayoutState(state: ScrollState) { | |
mScrollViewDelegate!!.setPullRelativeLayoutState(state) | |
} | |
override fun onScrollChanged(l: Int, t: Int, oldl: Int, oldt: Int) { | |
super.onScrollChanged(l, t, oldl, oldt) | |
mScrollViewDelegate!!.onScrollChanged(this, l, t, oldl, oldt) | |
} | |
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { | |
mScrollViewDelegate!!.onInterceptTouch(ev) | |
if (mScrollViewDelegate!!.moveY - mScrollViewDelegate!!.downY < 0) { | |
return super.onInterceptTouchEvent(ev) | |
} | |
if (scrollY == 0) { | |
val state = mScrollViewDelegate!!.scrollState | |
if (state === ScrollState.SHOW || state === ScrollState.MOVE) { | |
return false | |
} | |
} | |
return super.onInterceptTouchEvent(ev) | |
} | |
override fun onTouchEvent(ev: MotionEvent): Boolean { | |
return if (mScrollViewDelegate!!.scrollState === ScrollState.HIDE) { | |
false | |
} else super.onTouchEvent(ev) | |
} | |
fun setOnScrollChangedListener(listener: OnScrollChangedListener) { | |
mScrollViewDelegate!!.setOnScrollChangedListener(listener) | |
} | |
} |
Step 6: Creating the ScrollHeaderDelegate.kt file
You will use the ScrollShow set functions as shown below so as to enable the use of Header movement in the android application.
package dennis.pull.delegate | |
import android.view.View | |
class ScrollHeaderDelegate(private val mTargetView: View) : ScrollerDelegate(mTargetView) { | |
var isScrollShow: Boolean = false | |
private var mDuration = NORMAL_DURATION | |
fun scrollShow() { | |
if (!isScrollShow) { | |
return | |
} | |
val height = mTargetView.measuredHeight | |
if (height <= 0) { | |
return | |
} | |
smoothScrollTo(0, height, 0, -height, mDuration) | |
isScrollShow = false | |
} | |
fun setDuration(duration: Int) { | |
mDuration = duration | |
} | |
companion object { | |
private val NORMAL_DURATION = 800 | |
} | |
} |
Step 7: Creating the ScrollBodyDelegate.kt file
You will use the onTouchEvent function as shown below which is responsible for making objects move according to a preset program like the rotating triangle.
package dennis.pull.delegate | |
import android.view.MotionEvent | |
import android.view.View | |
import dennis.pull.ScrollState | |
import dennis.pull.listener.OnStateChangeListener | |
class ScrollBodyDelegate(private val mTargetView: View) : ScrollerDelegate(mTargetView) { | |
private var mMaxOffset: Int = 0 | |
private var mLastY: Float = 0.toFloat() | |
private var mMoveY: Int = 0 | |
var state = ScrollState.SHOW | |
private var mOnStateChangeListener: OnStateChangeListener? = null | |
fun setMaxOffset(offset: Int) { | |
mMaxOffset = offset | |
} | |
fun onTouchEvent(event: MotionEvent): Boolean { | |
if (state === ScrollState.HIDE) { | |
return false | |
} | |
val y = event.y | |
when (event.action) { | |
MotionEvent.ACTION_DOWN -> mLastY = y | |
MotionEvent.ACTION_MOVE -> { | |
val moveY = (y - mLastY).toInt() | |
if (mTargetView.scrollY <= 0 && moveY > 0) { | |
val offset = moveY / 2 | |
move(offset) | |
} | |
mLastY = y | |
} | |
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> changeState() | |
} | |
return true | |
} | |
private fun move(offset: Int) { | |
state = ScrollState.MOVE | |
if (mOnStateChangeListener != null) { | |
mOnStateChangeListener!!.pullViewMove(state, -offset) | |
} | |
mTargetView.scrollBy(0, -offset) | |
} | |
private fun hide() { | |
state = ScrollState.HIDE | |
if (mOnStateChangeListener != null) { | |
mOnStateChangeListener!!.pullViewHide(state) | |
} | |
mMoveY = mTargetView.measuredHeight + Math.abs(mTargetView.scrollY) | |
smoothScrollTo(0, mTargetView.scrollY, 0, -mMoveY, NORMAL_TIME * 3) | |
} | |
fun hide(time: Int) { | |
state = ScrollState.HIDE | |
if (mOnStateChangeListener != null) { | |
mOnStateChangeListener!!.pullViewHide(state) | |
} | |
mMoveY = mTargetView.measuredHeight + Math.abs(mTargetView.scrollY) | |
smoothScrollTo(0, mTargetView.scrollY, 0, -mMoveY, time) | |
} | |
private fun show() { | |
state = ScrollState.SHOW | |
if (mOnStateChangeListener != null) { | |
mOnStateChangeListener!!.pullViewShow(state) | |
} | |
smoothScrollTo(0, mTargetView.scrollY, 0, -mTargetView.scrollY, | |
mTargetView.scrollY) | |
} | |
private fun changeState() { | |
if (Math.abs(mTargetView.scrollY) > mMaxOffset + 50) { | |
hide() | |
} else { | |
show() | |
} | |
} | |
fun open() { | |
state = ScrollState.OPEN_START | |
if (mOnStateChangeListener != null) { | |
mOnStateChangeListener!!.pullViewOpenStart() | |
} | |
smoothScrollTo(0, -mMoveY, 0, mMoveY, NORMAL_TIME) | |
mTargetView.postDelayed({ | |
state = ScrollState.OPEN_FINISH | |
if (mOnStateChangeListener != null) { | |
mOnStateChangeListener!!.pullViewOpenFinish() | |
} | |
}, NORMAL_TIME.toLong()) | |
} | |
fun setOnStateChangeListener(listener: OnStateChangeListener) { | |
mOnStateChangeListener = listener | |
} | |
companion object { | |
private val NORMAL_TIME = 600 | |
} | |
} |
Step 8: Creating the MainActivity.kt file
Now, come to your MainActivity.java file. We will announce a function called initData () to display the Building Manager and Layout Adapter. Another important function that you will use in the main function is the initListener function. It is responsible for global app planning and multi-component mobility within the app.
Finally, your final MainActivity.java will be like this.
package com.dennis.doublepull | |
import android.graphics.drawable.AnimationDrawable | |
import android.os.Bundle | |
import android.view.View | |
import android.view.ViewTreeObserver | |
import android.widget.RelativeLayout | |
import androidx.appcompat.app.AppCompatActivity | |
import androidx.recyclerview.widget.LinearLayoutManager | |
import androidx.recyclerview.widget.RecyclerView | |
import com.dennis.doublepull.adapter.ScrollAdapter | |
import kotlinx.android.synthetic.main.main_in.* | |
import kotlinx.android.synthetic.main.main_out.* | |
import dennis.pull.ScrollState | |
import dennis.pull.listener.OnScrollChangedListener | |
import dennis.pull.listener.OnStateChangeListener | |
class MainActivity : AppCompatActivity(), OnStateChangeListener, | |
View.OnClickListener, OnScrollChangedListener { | |
private var mHeaderHeight: Int = 0 | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
setContentView(R.layout.activity_main) | |
initData() | |
initListener() | |
} | |
private fun initData() { | |
main_rcv.layoutManager = LinearLayoutManager(this) | |
main_rcv.adapter = ScrollAdapter() | |
} | |
private fun initListener() { | |
main_body.setOnStateChangeListener(this) | |
main_root.setOnScrollChangedListener(this) | |
main_open_iv.setOnClickListener(this) | |
main_rcv.addOnScrollListener(object : RecyclerView.OnScrollListener() { | |
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { | |
super.onScrolled(recyclerView, dx, dy) | |
if (main_body.state === ScrollState.HIDE) { | |
main_root.scrollBy(dx, dy) | |
} | |
} | |
}) | |
main_body.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { | |
override fun onGlobalLayout() { | |
mHeaderHeight = main_header.measuredHeight | |
val iconHeight = main_haibao.measuredHeight | |
val pullRelMarTop = mHeaderHeight - iconHeight / 2 | |
setPullRelativeLayoutMarTop(pullRelMarTop) | |
setContentViewMarTop(iconHeight / 2) | |
main_body.setMaxOffset(iconHeight / 2) | |
initOpenAnim() | |
main_body.viewTreeObserver.removeGlobalOnLayoutListener(this) | |
} | |
}) | |
} | |
private fun initOpenAnim() { | |
main_header.isScrollShow = true | |
main_body.hide(100) | |
main_body.open() | |
main_header.scrollShow() | |
val animationDrawable = main_open_iv.drawable as AnimationDrawable | |
animationDrawable.start() | |
} | |
private fun setPullRelativeLayoutMarTop(top: Int) { | |
val mPullLayoutParams = main_body.layoutParams as RelativeLayout.LayoutParams | |
mPullLayoutParams.setMargins(0, top, 0, 0) | |
} | |
private fun setContentViewMarTop(top: Int) { | |
val mContentViewParams = main_content.layoutParams as RelativeLayout.LayoutParams | |
mContentViewParams.setMargins(0, top, 0, 0) | |
} | |
override fun pullViewShow(state: ScrollState) { | |
main_root.setPullRelativeLayoutState(state) | |
main_info.visibility = View.VISIBLE | |
} | |
override fun pullViewHide(state: ScrollState) { | |
main_root.setPullRelativeLayoutState(state) | |
main_header.visibility = View.INVISIBLE | |
} | |
override fun pullViewMove(state: ScrollState, offset: Int) { | |
main_root.setPullRelativeLayoutState(state) | |
main_info.visibility = View.INVISIBLE | |
} | |
override fun pullViewOpenStart() { | |
if (main_header.isScrollShow) { | |
main_root.scrollTo(0, 0) | |
} | |
main_header.visibility = View.VISIBLE | |
main_info.visibility = View.VISIBLE | |
main_header_mask.visibility = View.INVISIBLE | |
} | |
override fun pullViewOpenFinish() { | |
main_header_mask.visibility = View.VISIBLE | |
val manager = main_rcv.layoutManager as LinearLayoutManager? | |
manager!!.scrollToPosition(0) | |
} | |
override fun onClick(v: View) { | |
if (v.id == R.id.main_open_iv) { | |
openOutUi() | |
} | |
} | |
private fun openOutUi() { | |
main_body.open() | |
main_header.scrollShow() | |
} | |
override fun onBackPressed() { | |
if (main_body.state === ScrollState.HIDE) { | |
openOutUi() | |
} else { | |
super.onBackPressed() | |
android.os.Process.killProcess(android.os.Process.myPid()) | |
} | |
} | |
override fun onScrollChange(v: View, scrollX: Int, scrollY: Int, oldScrollX: Int, oldScrollY: Int) { | |
val offset = (mHeaderHeight * 0.7).toInt() | |
if (scrollY > offset && main_body.state === ScrollState.HIDE) { | |
main_header.isScrollShow = true | |
} | |
} | |
} |
Above all, you can display any message you want in-app layout by customizing the respective parts. I have used simple texts to display the different sections in android. Hence, you can utilize different gadgets or symbols and you can likewise utilize this code anyplace in your application as indicated by your need.
Future Directions
Finally, the future directions involve displaying multiple pull layouts from different activities and enabling the different pull layouts by pressing a button rather than scrolling.
Learning Strategies and Tools
Zeroing in on the double pull layout is responsible for the both upwards pull and downwards pull in the same screen by scrolling rather than the single pull layout which is commonly used. In conclusion, we learned how your app can use dual drag creation on Android. Tasks now have to use different functions in their proper functions to use dual drag design on android.
Reflective Analysis
Figuring out how to utilize ScrollView managers in android was a great learning experience. It is an astonishing instrument that makes the comprehension ScrollView endpoints simple. A typical mistake that is normal while utilizing pull layout in android is the length of items inside it, so be cautious with that.
The most common use of duplicate drag editing in android mobile apps to facilitate communication with complex Android apps. This method is very useful for highlighting enough to be recognized. So, get a double drag design to do the required function on android.
In conclusion, I spent 72 hours completing the project and the blog. Finally, check everything here GitHub repository.
Link to the previous post: https://blog.ldtalentwork.com/2020/09/16/how-to-check-internet-connection-programatically-on-android-from-a-button-click-in-kotlin
That’s all for this tutorial!
A very informative article. I had so much fun reading it and building the App unto my phone.
A quick question. You mentioned that “A typical mistake that is normal while utilizing pull layout in android is the length of items inside it, so be cautious with that.” Is there a finite length to the items we can use inside of a pull layout?
There is no finite length to the items you can use inside a pull layout but it’s advisable to avoid screen disorientation
Awesome 👍
Thank you.
I forgot to mention that I did not encounter any errors while running your project on my computer and building the app to my phone.
Also, you did an awesome job outlining the steps to reproducing your result.
This really work for me.
Some developers have stated that they prefer developing apps for iOS over Android. However, opinion is not always universal, and one of the most prevalent reasons for choosing one over the other is the programming language with which the person is most familiar. If you’re thinking about taking iOS classes or learning to code for mobile platforms, the best option will always come down to your own preferences.