I am having significant difficulty in this and have tried everything imaginable. I am trying to make a card view draggable/swipeable vertically. All I want to accomplish is for the user to be able to swipe/drag the card view up and down. Please see attached photos-this is what I am trying to accomplish.
activity_main
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorPrimary"
tools:context=".MainActivity">
<androidx.cardview.widget.CardView
android:id="#+id/cardviewbackground"
android:layout_width="match_parent"
android:layout_height="350dp"
android:layout_marginTop="-175dp"
app:cardBackgroundColor="#color/white"
app:cardCornerRadius="18dp"
android:layout_marginStart="40dp"
android:layout_marginEnd="40dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</androidx.coordinatorlayout.widget.CoordinatorLayout>
MainActicity.kt
class MainActivity<T> : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
val listener = View.OnDragListener { view, event ->
when(event.action) {
DragEvent.ACTION_DRAG_STARTED -> {
event.clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)
}
else -> false
}
}
}
Related
I want my Snackbar to start the animation from the view in red and not at the bottom of the screen as by default, I wanted to give the feeling that the snackbar is appearing behind this view, unfortunately, I don't get good results.
Note: The project I'm working on is not using coordinatorlayout, but I can use.
Activity
class MainActivity : AppCompatActivity() {
private lateinit var snackbar : Snackbar
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
with(binding) {
val view = root
setContentView(view)
snackbar = Snackbar.make(this#MainActivity, view, "Teste", Snackbar.LENGTH_SHORT)
snackbar.anchorView = textText
snackbar.animationMode = BaseTransientBottomBar.ANIMATION_MODE_SLIDE
btnSnackbar.setOnClickListener {
snackbar.show()
}
}
}
}
Layout
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="#+id/btn_snackbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:text="#string/snackbar" />
<TextView
android:id="#+id/text_text"
android:layout_width="match_parent"
android:layout_height="70dp"
android:background="#color/design_default_color_error"
android:gravity="center"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
com.google.android.material:material:1.7.0
I tried using elevation to override the snackbar and it doesn't work either.
I have an issue with animations. I have defined two simple layouts:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/root"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/player1"
android:layout_width="50dp"
android:layout_height="77dp"
android:layout_marginStart="32dp"
android:layout_marginTop="16dp"
android:src="#drawable/two_hearts"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
looking like this:
and
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/root"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/player1"
android:layout_width="50dp"
android:layout_height="77dp"
android:layout_marginStart="32dp"
android:layout_marginTop="16dp"
android:src="#drawable/two_hearts"
android:translationY="-60dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
with looks:
The difference between them is android:translationY="-60dp".
I switch between them in the GameFragment.kt:
class GameFragment: Fragment() {
private lateinit var root: ConstraintLayout
private var expanded = false
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
root = inflater.inflate(R.layout.fragment_game_hidden, container, false) as ConstraintLayout
root.setOnClickListener {
show()
}
return root
}
private fun show() {
if (expanded) {
updateConstraints(R.layout.fragment_game_hidden)
} else {
updateConstraints(R.layout.fragment_game_shown)
}
expanded = !expanded
}
private fun updateConstraints(#LayoutRes id: Int) {
val newConstraintSet = ConstraintSet()
newConstraintSet.clone(context, id)
val transition = ChangeBounds()
transition.interpolator = AccelerateDecelerateInterpolator()
transition.duration = 1000
newConstraintSet.applyTo(root)
TransitionManager.beginDelayedTransition(root, transition)
}
}
My problem is, the animation is not played at all, the card on the layout just jumps to target location without any transition. Why? I feel like I'm missing something obvious here...
Remove android:translationY="-60dp" in : fragment_game_hidden.xml . Just use margin directly.
Try change to code below:
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/root"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/player1"
android:layout_width="50dp"
android:layout_height="77dp"
android:layout_marginStart="32dp"
android:src="#drawable/two_hearts"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
I was able to create a bottom sheet card view. I am trying convert what I haveto a top sheet card view(when user clicks button, card view should appear from the top, not bottom). Any assistance is appreciated.
SheetBehaviorActivity.kt
class SheetBehaviourActivity : AppCompatActivity() {
lateinit var sheetBehavior: BottomSheetBehavior<*>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val btn: Button = this.findViewById(R.id.testing_bottom_sheet_btn)
btn.setOnClickListener {
if (sheetBehavior.state != BottomSheetBehavior.STATE_EXPANDED) {
sheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
btn.text = "Close sheet"
} else {
sheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
btn.text = "Expand sheet"
}
}
val sheetContainer = findViewById<CardView>(R.id.bottom_sheet_container)
sheetBehavior = BottomSheetBehavior.from(sheetContainer)
sheetBehavior.isHideable = true
sheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.SheetBehaviourActivity">
<Button
android:id="#+id/testing_bottom_sheet_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Expand Sheet" />
<android.support.v7.widget.CardView
android:id="#+id/bottom_sheet_container"
android:layout_width="match_parent"
app:cardCornerRadius="20dp"
app:cardBackgroundColor="#android:color/holo_blue_light"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior"
android:layout_height="300dp"/>
</android.support.design.widget.CoordinatorLayout>
This is TopSheetBehavior implementation topSheetLayout work fine in my case
Here is how you do it :
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.SheetBehaviourActivity">
<Button
android:id="#+id/testing_bottom_sheet_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Expand Sheet" />
<android.support.v7.widget.CardView
android:id="#+id/bottom_sheet_container"
android:layout_width="match_parent"
app:cardCornerRadius="20dp"
app:cardBackgroundColor="#android:color/holo_blue_light"
app:behavior_hideable="true"
app:behavior_peekHeight="56dp"
app:layout_behavior="your.package.components.TopSheetBehavior"/>
</android.support.design.widget.CoordinatorLayout>
This TopSheetBehavior is just replacing the behavior parameter
Hi I am working on showing a view (publisher ad) below recycler view. Recycler view has some empty views to show publisher ad when it comes to related index. Currently I can click on any recycler view item and I am able to see my ad, but I cannot click on ad. How to achieve this? Here is my layout:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:ads="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:orientation="vertical">
<com.google.android.gms.ads.doubleclick.PublisherAdView
android:id="#+id/ad_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:focusable="true"
android:focusableInTouchMode="true"
ads:adSize="SMART_BANNER"
ads:adUnitId="#string/parallax_ad_unit_id"/>
<widgets.AppSwipeRefreshLayout
android:id="#+id/swipe_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<widgets.AppRecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" />
</widgets.AppSwipeRefreshLayout>
</FrameLayout>
As you are using FrameLayout the PublisherAdView will be overlapped by AppRecyclerView or AppSwipeRefreshLayout, so when you click on item, that click event will be listened by `AppRecyclerView1 every time.
Try after changing the position of your RecyclerView and AdView
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:ads="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:orientation="vertical">
<widgets.AppSwipeRefreshLayout
android:id="#+id/swipe_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<widgets.AppRecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" />
</widgets.AppSwipeRefreshLayout>
<com.google.android.gms.ads.doubleclick.PublisherAdView
android:id="#+id/ad_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:focusable="true"
android:focusableInTouchMode="true"
ads:adSize="SMART_BANNER"
ads:adUnitId="#string/parallax_ad_unit_id"/>
</FrameLayout>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ads="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:orientation="vertical">
<widgets.AppSwipeRefreshLayout
android:id="#+id/swipe_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<widgets.AppRecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:nestedScrollingEnabled="false"
android:scrollbars="vertical"
ads:layout_constraintBottom_toBottomOf="parent"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintTop_toBottomOf="#id/ad_view" />
<com.google.android.gms.ads.doubleclick.PublisherAdView
android:id="#+id/ad_view"
android:layout_width="match_parent"
android:layout_height="0dp"
ads:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</widgets.AppSwipeRefreshLayout>
Use a RelativeLayout for activity_main.xml which allows to place views
on top of others (in Z axis).
ex: SwipeRefreshLayout is the top most view here.
<RelativeLayout 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:focusable="true"
tools:context=".MainActivity">
<View
android:id="#+id/ad"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorAccent" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="#+id/swipe_refresh_posts"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_posts"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</RelativeLayout>
Create two item layouts for two types of recycler view types
ex:
item_normal.xml, item_transparent.xml (where layout background is transparent that allow to see the layout below the surface area)
Setting android:clickable=false in item_transparent does not stop triggering the click event on transparent item, so use communication flow using interfaces, to bring the other view(ad) to the front when clicked on transparent item.
MainActivity.kt
class MainActivity : AppCompatActivity(), RvAdpater.OnItemClick {
private lateinit var swipeRefreshLayout: SwipeRefreshLayout
private lateinit var adView: View
private lateinit var rvPosts: RecyclerView
override fun onClick() {
bringAdFront()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupRecyclerView()
setupSwipeRefreshLayout()
}
private fun setupRecyclerView() {
rvPosts = findViewById(R.id.rv_posts)
rvPosts.layoutManager = LinearLayoutManager(this)
val rvAdpater = RvAdpater()
rvAdpater.setListener(this)
rvPosts.adapter = rvAdpater
}
private fun setupSwipeRefreshLayout() {
swipeRefreshLayout = findViewById(R.id.swipe_refresh_posts)
swipeRefreshLayout.setOnRefreshListener {
Log.d(TAG, "Refreshing...")
}
}
private fun bringAdFront() {
adView = findViewById<View>(R.id.ad)
adView.bringToFront()
}
// to go back to the normal recycler view(which is inside swipe refresh layout)
// when back button is pressed
override fun onBackPressed() {
val parent = swipeRefreshLayout.parent as ViewGroup
parent.removeAllViews()
parent.addView(adView, 0)
// at last add swipe refresh layout which is the container of the recyclerview
parent.addView(swipeRefreshLayout, 1)
}
}
RvAdapter.kt
const val TAG = "RvAdpater"
class RvAdpater : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private lateinit var listener:OnItemClick
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val viewNormal = LayoutInflater.from(parent.context).inflate(R.layout.item_normal, parent, false)
val viewTransparent = LayoutInflater.from(parent.context).inflate(R.layout.item_transparent, parent, false)
return when(viewType){
0 -> NormalViewHolder(viewNormal)
2 -> TransparentViewHolder(viewTransparent)
else -> NormalViewHolder(viewNormal)
}
}
override fun getItemCount(): Int = 10
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when(getItemViewType(position)){
0 -> {
val normalHolder = holder as NormalViewHolder
normalHolder.tv.text = "Post"
normalHolder.itemView.setOnClickListener {
Log.d(TAG, "Clicked on Normal item")
}
}
2 -> {
val transparentHolder = holder as TransparentViewHolder
transparentHolder.itemView.setOnClickListener {
listener.onClick()
}
}
}
}
fun setListener(onItem:OnItemClick){
listener = onItem
}
interface OnItemClick{
fun onClick()
}
override fun getItemViewType(position: Int): Int = position % 2 * 2
class NormalViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
val tv:TextView = itemView.findViewById(R.id.tv_post)
}
class TransparentViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)
}
Checkout branch: with-swipe-layout
I am currently learning about the Conductor framework for Android and have a bit of a problem or misunderstanding of how it works.
I was under the impression that the method
setRetainViewMode(RetainViewMode.RETAIN_DETACH);
would save the states of the views in the controller. To test the behaviour, I added EditText views, entered a value in it and rotated the screen. I also added 2 views with onclick listeners attached, changing the background color onclick
The result of the test was that the EditText views maintained the state and preserved the entered values. But the 2 views, changed back to their original background color (none).
This is the behaviour of the views regardless on which RetainViewMode is set
I have this simple MainActivity (note: I'm writing in Kotlin):
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var router: Router = Conductor.attachRouter(this, controller_container, savedInstanceState)
if (!router.hasRootController()) {
var t : TestController = TestController()
t.retainViewMode = Controller.RetainViewMode.RETAIN_DETACH
router.setRoot(RouterTransaction.with(t))
}
}
companion object doTask {
fun start(activity : Activity) {
val intent = Intent(activity, MainActivity::class.java)
activity.startActivity(intent)
}
}
}
And here is the the TestController:
class TestController : BaseController() {
var i : Int = 0
var h : Int = 0
override fun onViewBound(view: View) {
view.a.setOnClickListener {
i++
if (i % 2 == 0) {
view.a.setBackgroundColor(ContextCompat.getColor(applicationContext, R.color.white))
} else {
view.a.setBackgroundColor(ContextCompat.getColor(applicationContext, R.color.turtle_green))
}
}
view.b.setOnClickListener {
h++
if (h % 2 == 0) {
view.b.setBackgroundColor(ContextCompat.getColor(applicationContext, R.color.white))
} else {
view.b.setBackgroundColor(ContextCompat.getColor(applicationContext, R.color.blue_light))
}
}
}
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
return inflater.inflate(R.layout.controller_layout_test, container, false)
}
}
And xml layout file controller_layout_test:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<requestFocus></requestFocus>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/logo_simple"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="300dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="30dp"
android:layout_gravity="center"
android:background="#color/transparent50p"
android:padding="20dp">
<EditText
android:id="#+id/gt"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_gravity="center"
android:layout_marginTop="10dp"
android:padding="6dp"
android:background="#color/white_transparent50p"/>
/>
<EditText
android:id="#+id/erergeargf"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_gravity="center"
android:layout_marginTop="10dp"
android:padding="6dp"
android:background="#color/white_transparent50p"/>
<View
android:id="#+id/a"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_margin="5dp"
android:layout_gravity="center"></View>
<View
android:id="#+id/b"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_margin="5dp"
android:layout_gravity="center"></View>
</LinearLayout>
</ScrollView>
</FrameLayout>
activity_main xml layout looks like this:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="dk.minreklame.minetilbud_v2.MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
/>
</android.support.design.widget.AppBarLayout>
<com.bluelinelabs.conductor.ChangeHandlerFrameLayout
android:id="#+id/controller_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
/>
</android.support.design.widget.CoordinatorLayout>
As your views have references to the host Activity, they will never be retained across orientation changes. That would cause a memory leak. The docs on RETAIN_DETACH state:
The Controller will retain its reference to its view when detached, but will still release the reference when a config change occurs.