How to save scroll of Scroll View properly - android

How to save scroll state of scrollview properly.In my code, I'm using :
scroll_x = scrollView.getScrollX();
scroll_y = scrollView.getScrollY();
when activity pause,i'm stored x and y as you can see here, and when activity start, i'm scroll scrollView to x and y.
But crux is (main problem) is, scrollview not scrollview to x and y properly, it scroll up or down a little bit automatically. How to fix it?

You can manage the instance state by using this class:
class SaveScrollNestedScrollViewer : NestedScrollView {
constructor(context: Context) : super(context)
constructor(context: Context, attributes: AttributeSet) : super(context, attributes)
constructor(context: Context, attributes: AttributeSet, defStyleAttr: Int) : super(context, attributes, defStyleAttr)
public override fun onSaveInstanceState(): Parcelable? {
return super.onSaveInstanceState()
}
public override fun onRestoreInstanceState(state: Parcelable?) {
super.onRestoreInstanceState(state)
}
}
use on your xml:
<yourClassNamePlace.SaveScrollNestedScrollViewer
android:id="#+id/my_scroll_viewer"
android:layout_width="match_parent"
android:layout_height="match_parent">
</yourClassNamePlace.SaveScrollNestedScrollViewer>
and then use in activity like this:
class MyActivity : AppCompatActivity() {
companion object {
var myScrollViewerInstanceState: Parcelable? = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.my_activity)
if (myScrollViewerInstanceState != null) {
my_scroll_viewer.onRestoreInstanceState(myScrollViewerInstanceState)
}
}
public override fun onPause() {
super.onPause()
myScrollViewerInstanceState = my_scroll_viewer.onSaveInstanceState()
}
}

Related

onGenericMotion in view isn't seeing joystick events

I assume I'm missing something simple, but I can't seem to get joystick actions (X,Y,Z,RZ) from a game pad using View.OnGenericMotionListener in Android API 30. I followed an example (I thought) but joystick motions are undetected. If I replace OnGenericMotionListener and my onGenericMotion functions with OnTouchListener and onTouch I see exactly what I would expect from touches of the touchpad. In the code below I have reduced to a minimum to see the issue. With joystick = BasicMotion I do not get my debug log messages- I only get
I/ViewRootImpl#cc64931[MainActivity]: ViewPostIme key 1
If I change to joystick = BasicTouch I get my debug log.
What am I missing to get input from a game pad? main_activity and BasicMotion/BasicTouch classes below. Also note if I add the onGenericMotionEvent function to MainActivity, then I see joystick inputs there, so I know the game pad is working. Code is in Kotlin, but I'd be happy with a java solution.
class MainActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
// val joystick = BasicMotion(this)
val joystick = BasicTouch(this)
setContentView(joystick)
super.onCreate(savedInstanceState)
}
}
BasicMotion class:
class BasicMotion : SurfaceView, SurfaceHolder.Callback, View.OnGenericMotionListener {
constructor(context: Context?) : super(context) {
holder.addCallback(this)
setOnGenericMotionListener(this)
}
constructor(context: Context?, attributes: AttributeSet?) : super(context, attributes) {
holder.addCallback(this)
setOnGenericMotionListener(this)
}
constructor(context: Context?, attributes: AttributeSet?, style: Int) : super(
context, attributes, style
) {
holder.addCallback(this)
setOnGenericMotionListener(this)
}
override fun surfaceCreated(holder: SurfaceHolder) {}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}
override fun surfaceDestroyed(holder: SurfaceHolder) {}
override fun onGenericMotion(v: View, event: MotionEvent): Boolean {
Log.d("Debug Motion", "detected")
return true
}
}
Equivalent BasicTouch class:
class BasicTouch : SurfaceView, SurfaceHolder.Callback, View.OnTouchListener {
constructor(context: Context?) : super(context) {
holder.addCallback(this)
setOnTouchListener(this)
}
constructor(context: Context?, attributes: AttributeSet?) : super(context, attributes) {
holder.addCallback(this)
setOnTouchListener(this)
}
constructor(context: Context?, attributes: AttributeSet?, style: Int) : super(
context, attributes, style
) {
holder.addCallback(this)
setOnTouchListener(this)
}
override fun surfaceCreated(holder: SurfaceHolder) {}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}
override fun surfaceDestroyed(holder: SurfaceHolder) {}
override fun onTouch(v: View, event: MotionEvent): Boolean {
Log.d("Debug Touch", "detected")
return true
}
}

Redrawing Canvas in Android custom View

I am trying to set up a canvas to redraw whenever the user clicks a button. Whenever I trigger the redraw via invalidate() the app freezes and crashes. I am very new to android development so I am struggling to see what I neglecting. Any insight would be appriciated.
The relevent classes are included below.
Thanks,
mainactivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun submit(view: View) {
val text = findViewById<View>(R.id.editTextNumberDecimal) as EditText
val canvas = findViewById<View>(R.id.customCanvas) as CustomCanvas
val value = text.text.toString()
val t = findViewById<View>(R.id.textView) as TextView
t.text = value
canvas.setWeightandRedraw(value.toFloat())
}
}
canvas
class CustomCanvas : View {
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
}
var weight: Float = 0.0f;
override fun onDraw(canvas: Canvas?) {
drawBar(canvas);
drawWeightKg(canvas, this.weight)
}
fun setWeightandRedraw(_weight: Float) {
weight = _weight;
invalidate();
}
fun drawBar(canvas: Canvas?) {
.........
}
fun drawWeightKg(canvas: Canvas?, weight: Float) {
.........
}
}
Moving invalidate() to onDraw() appeard to fix this.
override fun onDraw(canvas: Canvas?) {
drawBar(canvas);
drawWeightKg(canvas, this.weight)
invalidate();
}

How to implements a custom view within some fragments?

I want to implement a custom view within some Fragments, and this view doesn't have any dependency on activity, this way I can share it as a common UI component to others.
But I have a problem when trying to use FragmentManager to manage those Fragments inside the custom view, how can I get a FragmentManager instance? Generally, we can get it from Activity, but in my case, what should I do would be more reasonable?
If your custom view is inflated by a fragment you have access to getParentFragmentManager there is more info here
But I have a problem when trying to use FragmentManager to manage those Fragments inside the custom view, how can I get a FragmentManager instance? Generally, we can get it from Activity, but in my case, what should I do would be more reasonable?
The best way to do it is to define an interface in the view to describe what events it has, then the fragment that hosts this view will implement this interface and actually handle it.
fun View.onClick(clickListener: (View) -> Unit) {
setOnClickListener(clickListener)
}
class MyView: FrameLayout {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
interface Listener {
fun onSomeEvent(someData: SomeData)
}
var listener: Listener? = null
override fun onFinishInflate() {
super.onFinishInflate()
val binding = MyViewBinding.bind(this)
with(binding) {
someButton.onClick {
listener?.onSomeEvent(someData)
}
}
}
}
And then
class MyFragment: Fragment(R.layout.my_fragment), MyView.Listener {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = MyFragmentBinding.bind(view)
with(binding) {
myView.listener = this#MyFragment
}
}
}

how to set property as val at customview written by xml

In case of below, MyView's callback is not mutable property so I want to declare as val.
Not only that, Since it can not be initialized at init function, it is error prone.
How to set callback as val or... how to set callback in the init function?
Thanks
class MyView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) {
lateinit var callback: Callback
init {
//callback =
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
setContentView(R.layout.main_activity)
my_view.callback = object : Callback {
}
}
}
<LinearLayout>
<com.example.MyView
android:id="#+id/my_view/>
</LinearLayout>
If you want to set callback from another class, e.g. Activity, this property must be mutable:
class MyView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) {
var callback: Callback? = null
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
setContentView(R.layout.main_activity)
my_view.callback = object : Callback {
}
}
}
If you want callback to be immutable you should initialize it in MyView class:
class MyView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) {
val callback: Callback = object : Callback {
}
}
But in this case you won't be able to update it from another class.

Kotlin add custom listener for clicked on widgets on android

I'm newbie in Kotiln and this code is simple class which I'm using on Android. In this class I want to make simple listener to detect clicks on item inside of a class such as activity, fragment or adapter. My problem is that I can define this listener for circularProgressView widget.
class DownloadProgressView : RelativeLayout {
var progressListener: ((downloading: Boolean) -> Unit)? = null
private var downloading = false
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() {
LayoutInflater.from(context).inflate(R.layout.download_progress_layout, this)
circularProgressView.setOnClickListener {
downloading = !downloading
setProgressViews(downloading)
progressListener?.invoke(downloading)
//if listener!=null then onItemClicked.onClick();
}
}
}
How can I know if user clicked on circularProgressView inside activity?
I'm trying to implement this interface and use it inside Kotlin:
public interface OnItemClickListener {
void onClick(int position);
}
For init progreessListener use following code
var listener: () -> Unit = {}
For call onClick use : listener()
For implement your interface use following code :
var onItemClickInterface : () -> Unit = {
//TODO
}

Categories

Resources