I want to create a barcode scanner activity, I found this nice tutorial:
http://www.devexchanges.info/2016/10/reading-barcodeqr-code-using-mobile.html
How can I decorate the preview image with detected area (with dots or rectangle).
Put another transparent view on top of your SurfaceView and draw on that. B oth views will loosely coupled, so there is some math involved in getting it right to match the decoration with the camera output.
So in your activity_main.xml:
<?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"
android:padding="16dp">
<SurfaceView
android:id="#+id/surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true" />
<com.yourApp.YourDecoratorView
android:id="#+id/decorator_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true" />
...
</RelativeLayout>
And then, create a custom view:
class YourDecoratorView #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
... initializing code
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
// your code
canvas?.drawLine(0f, 0f, 0f, paint)
}
}
If it is just static visuals you want to show, use an ImageView instead of a custom view and create the drawable you want.
Related
I have a bunch of Buttons in a LinearLayout. In the LinearLayout, I override onDraw() to call canvas.scale(scale, scale) where scale is some number. The buttons in the LinearLayout scales visually but the click area doesn't move and scale with the visual representation. In other words, the click area registers in the original position after scaling
Here is a demo where a slider is used to scale the canvas. After scaling, you can see the button shows it's clicked animation only when clicked in the original position
layout.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.slider.Slider
android:id="#+id/mySlider"
android:valueFrom=".5"
android:valueTo="3"
android:value="1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.dgnt.playground.MyLinearLayout
android:id="#+id/myContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="My Testing String Is A long one My Testing String Is A long one" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="My Testing String Is A long one My Testing String Is A long one" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="My Testing String Is A long one My Testing String Is A long one" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="My Testing String Is A long one My Testing String Is A long one" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="My Testing String Is A long one My Testing String Is A long one" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="My Testing String Is A long one My Testing String Is A long one" />
</com.dgnt.playground.MyLinearLayout>
</LinearLayout>
</layout>
Custom LinearLayout
class MyLinearLayout : LinearLayout {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(
context,
attrs,
defStyle
)
init {
setWillNotDraw(false)
}
private var scale: Float = 1.0f;
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.scale(scale, scale)
}
fun setScale(value: Float) {
scale = value
invalidate()
requestLayout()
}
}
Main Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ActivityMainBinding.inflate(layoutInflater).apply {
setContentView(root)
mySlider.addOnChangeListener { slider, value, fromUser ->
myContainer.setScale(value)
}
}
}
}
I tried instead overriding dispatchTouchEvent to figure out if the touch coordinates of the user intercepts the view but it seems like view.getLocationOnScreen() doesn't take into consideration the scale of the canvas either as evidenced by this question Android: Get View Location on Screen after View is scaled?
What can I do so that the click/touch registers exactly where the view is before and after scaling the canvas?
One way to fix this is to scale your touch events, I've done this in a custom view I created that has scaling and panning.
In your MyLinearLayout override dispatchTouchEvent
Create a Matrix that applies the scale factor then transform the MotionEvent with the scale Matrix before called the original dispatchTouchEvent
sorry in Java but something like (a bit from memory and not exactly how I did it as that had some more complicated use cases for panning and different pans for different parts of the screen)
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Matrix matrix = new Matrix();
Matrix mappingMatrix = new Matrix();
matrix.setScale(scaleFactor, scaleFactor);
// From memory the Matrix for the transform has to be inverted
matrix.invert(mappingMatrix);
// Copy motion event because I seem to remember you cannot change the original motion event
MotionEvent transformEvent = MotionEvent.obtain(ev);
transformEvent.transform(mappingMatrix);
// call parent class so this processes the modified event as normal
boolean returnVal = super.dispatchTouchEvent(transformEvent);
// Tidy up the copied motion event to not leak memory
transformEvent.recycle();
return returnVal;
}
I have a custom view like this:
class SquareView : View {
private lateinit var mRectanglePaint: Paint
constructor(context: Context?) : super(context) {
init(null)
}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
init(attrs)
}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
init(attrs)
}
constructor(
context: Context?,
attrs: AttributeSet?,
defStyleAttr: Int,
defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes) {
init(attrs)
}
private fun init(attrs: AttributeSet?) {
mRectanglePaint = Paint().apply {
isAntiAlias = true
color = Color.RED
}
}
override fun onDraw(canvas: Canvas) {
canvas.apply {
drawRectangle(this)
}
}
private fun drawRectangle(canvas: Canvas) {
val rect = Rect()
rect.left = 100
rect.right = rect.left + 100
rect.top = 100
rect.bottom = rect.top + 100
canvas.drawRect(rect, mRectanglePaint)
}
}
And, this is my blue_square.xml file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:orientation="vertical"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_height="wrap_content">
<View
android:id="#+id/blue_square"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#0000FF" />
</LinearLayout>
I want to add this XML layout to my view and set position to bottom of the red square.
I added this code but this time only the blue square appears.
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val view = inflater.inflate(R.layout.blue_square, this, true)
I want to include the blue square in my custom view and change its position.
How can I do that? Thank you for your help.
When I said 2 different approaches, so the first is in a bit more detail but I'll also outline the others.
1)The first is to inflate and layout the xml to a Bitmap and then draw that Bitmap to you Canvas in the right place.
Inflate your xml BUT don't attach it or parent it to your custom view (as that really replaces you custom view (second and third parameters of inflate)
e.g.
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val xmlView = inflater.inflate(R.layout.blue_square, null, false)
Draw this new view to Bitmap
Sorry this is Java but you can convert it to Kotlin as you seem to be using that.
Canvas bitmapCanvas = new Canvas();
Bitmap bitmap = Bitmap.createBitmap(xmlView.getWidth(), xmlView.getHeight(), Bitmap.Config.ARGB_8888);
bitmapCanvas.setBitmap(bitmap);
xmlView.draw(bitmapCanvas);
Then in your onDraw draw this Bitmap to your canvas in the right position (setting top and left location to be below your red square)
Something like
plainPaint = Paint().apply { isAntiAlias = true }
canvas.drawBitmap (bitmap, left, top, plainPaint)
Note this is not exactly tested but it is similar to something I do a lot.
2)Don't invert the normal hierarchy of Views and ViewGroups, don't make your SquareView custom View be the single view your an App draws. Your SquareView custom view should just draw a view big enough to be the square (You should implement onMeasure for your custom view).
Then an App could do in it's xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:orientation="vertical"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_height="wrap_content">
<SquareView
android:id="#+id/custom_square"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<View
android:id="#+id/blue_square"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#0000FF" />
</LinearLayout>
HI im currently trying to achieve something similar to the given image.
can anyone guide me through how can I get the look in Kotlin/xml view?
Part I need to design is the Price Tag view.
much appreciated
Well, first of all, you can use any graphic software to draw the text background maybe photoshop or adobe illustrator for vectors. I'm not very good at this software but I created a vector image that is quite similar to what we actually want.
So in your drawables directory create a new drawable file, call it tv_bg.xml, then copy-paste this vector drawable.
tv_bg.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="120dp"
android:height="50dp"
android:autoMirrored="true"
android:viewportWidth="196.739"
android:viewportHeight="101.01">
<path
android:fillColor="#fff"
android:pathData="M196.736,0.01L196.736,99.993L0,99.993s8.6,11.826 15.051,-45.153 93.532,-54.829 93.532,-54.829Z" />
</vector>
After that create a layout file, I called it my_layout.xml.
my_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:background="#android:color/white"
android:padding="8dp"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/imageView"
android:layout_width="match_parent"
android:layout_height="340dp"
android:scaleType="centerCrop"
android:src="#drawable/your_image_placeholder"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="$ 15.00"
android:textSize="18sp"
android:textStyle="bold"
android:gravity="center"
android:background="#drawable/tv_bg"
android:textColor="#android:color/black"
app:layout_constraintBottom_toBottomOf="#+id/imageView"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
And that will be your final result
Of course, you need to improve the TextView background and text style a little to match what you actually want.
If I were to implement it, I would
customize a textView (let's call it, curvedTextView). Perhaps inside onDraw method, I would draw a background that resemblances the carved part (the background of 15,000). Some coordinate geometry would do the trick. The shape lools like a tangent curve, exponential curve, or simply 2 circles with a common tangent... just pick one.
Once you have your view, simply use constraintlayout, imageView, and place the curvedTextView to bottomRignt corner.
I need to write some codes, and do some trials to come up with a sample code. I'll try to update the answer later. Sorry for the inconvenience.
Update
So after writing some codes, here is my curvedTextView
package com.applications.ui
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Path
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.content.res.ResourcesCompat
import ridmik.one.R
class CurvedTextView #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : AppCompatTextView(context, attrs) {
val TAG = "CurvedTextView"
val white = ResourcesCompat.getColor(context.resources, R.color.colorSolidWhite, null)
val transparentColor = ResourcesCompat.getColor(context.resources, R.color.transparent, null)
val paint: Paint = Paint().apply {
this.color = white
this.style = Paint.Style.FILL_AND_STROKE
}
val path: Path = Path()
val paddingVertical: Float = getPxFromDp(context, 10.0).toFloat()
val paddingHorizontal: Float = getPxFromDp(context, 20.0).toFloat()
val delta: Float = getPxFromDp(context, 10.0).toFloat() // random trial and error
init{
}
fun getPxFromDp(context: Context, dp: Double): Double {
return dp * context.resources.displayMetrics.density
}
override fun draw(canvas: Canvas?) {
if(canvas!=null) {
path.moveTo(paddingHorizontal+delta, 0f)
path.cubicTo(
(paddingHorizontal - delta), delta,
delta, height - delta,
0f, height.toFloat()
)
path.lineTo(width.toFloat(), height.toFloat())
path.lineTo(width.toFloat(), 0f)
path.lineTo(paddingHorizontal+delta, 0f)
canvas.drawPath(path, paint)
}
super.draw(canvas)
}
}
Now simply add inside your constraintLayout:
<ImageView
app:layout_constraintBottom_toBottomOf="parent"
android:background="#FF0000"
android:layout_width="match_parent"
android:layout_height="200dp"/>
<com.applications.ui.CurvedTextView
android:id="#+id/curvedTextView"
android:text="$ 15,000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:paddingVertical="10dp"
android:paddingHorizontal="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Output:
Now that you have the view, try tweaking the variables to make it precisely like your requirement.
The simplest way I found to create a custom view, so I don't have to handle annoying things like to override the onLayout() method, is to make it inherit from a LinearLayout. I also have a LinearLayout at the root of the associated XML file that I inflate, so there is 2 of them at the root.
How can I optimise this, by removing one of this extra LinearLayout, but keep it simple to create custom views ?
MyToolbar.kt:
class MyToolbar #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) :
LinearLayoutCompat(context, attrs, defStyleAttr) {
private val binding = MyToolbarBinding.inflate(LayoutInflater.from(context), this, true)
init {
// [...] Initialization of my view ...
}
}
my_toolbar.xml:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<!-- Actual content of my view -->
</LinearLayout>
What I have right now after #Pawel and #Cata suggestion. This doesn't work, the LinearLayout use the whole height of the parent, but it should only wrap its content.
MyToolbar.kt:
class MyToolbar #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) :
LinearLayoutCompat(context, attrs, defStyleAttr) {
private val binding
init {
// Tried to add the attributes here as they seems ignored on the `merge` tag
gravity = Gravity.CENTER_VERTICAL
orientation = HORIZONTAL
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
binding = MyToolbarBinding.inflate(LayoutInflater.from(context), this)
// [...] Initialization of the view ...
}
}
my_toolbar.xml:
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<!-- Actual content of the view -->
</merge>
As Pawel suggested you could use merge to do that.
This is a sample from the developer website:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Here you can add your custom content views.. -->
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/add"/>
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/delete"/>
</merge>
I am trying to create an overlay for the zxing barcode scanner with Xamarin in Visual Studio, but I dont understand how to actually implement it.
I created a small layout for it, overlay.axml, which I want to be drawn on top of the camera view.
Overlay.axml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:minWidth="25px"
android:minHeight="25px">
<Space
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/space1"
android:layout_weight="1" />
<ToggleButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/tbtnTorch"
android:layout_weight="0"
android:textOn="Torch On"
android:textOff="Torch Off" />
</LinearLayout>
Additionally, I think I need to create a class that inherits from View to bind the layout to it, but im unsure about that, this is what I got so far.
Overlay.cs:
public Overlay(Context context) : base(context)
{
}
public Overlay(Context context, IAttributeSet attrs) : base(context, attrs)
{
}
public Overlay(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
{
}
protected override void OnDraw(Android.Graphics.Canvas canvas)
{
base.OnDraw(canvas);
}
I now would like to bind the overlay to my ZXing.scanner instance, but I am not sure how to do this. This is what I got so far.
MainActivity.cs:
scanner = new MobileBarcodeScanner(this);
options = new MobileBarcodeScannerOptions();
scanner.UseCustomOverlay = true;
scanner.CustomOverlay = new Overlay(this);
[...]
var result = await scanner.scan(options);
It works just as expected when not using an overlay, but with the overlay I just get a black screen when I start to scan. EDIT: Black Screen on Emulator, no overlay at all (like expected ?) on real device.
You don't need to create another Overlay class
Just inflate the overlay like this:
var zxingOverlay = LayoutInflater.FromContext(<YOUTCONTEXT>).Inflate(Resource.Layout.overlay, null);
and this to assign it to your scanner
scanner.CustomOverlay = zxingOverlay;