This maybe a weird question but I want to ask. I used CameraX in my application and for zoom in-out functionality, I used this code snippet:
val mListener = object :ScaleGestureDetector.SimpleOnScaleGestureListener() {
override fun onScale(detector: ScaleGestureDetector?): Boolean {
val currentZoomRatio = cameraInfo.zoomState.value?.zoomRatio ?: 0F
val delta = detector!!.scaleFactor
cameraControl.setZoomRatio(currentZoomRatio *delta)
return true
}
}
scaleDetector = ScaleGestureDetector(context, mListener)
binding.cameraView.setOnTouchListener() {
scaleDetector.onTouchEvent(event)
return true
}
My question is when I zoom in and out, there is a 0-500 ms delay/latency between user touch and camera zoom in/out. Do you think this is the default behaviour of zoom in-out ?
Related
I am working on Android app that has to put frame and Logo on Image at a Time, The Problem I am facing is Frame is Coming from first Fragment and the Logo from Second Fragment. I am Setting the Frame on Bitmap image on imageView as well as Logo.
The Issue I am facing is, as I successfully Successfully Add Frame on Bitmap image, and I try to also set Logo on Bitmap image it Remove the Frame and Set the Logo on Bitmap and vice versa..
What I really want is Frame and Logo are set on Bitmap at a time...
Here, where Logo Coming From First Fragment Adapter to main Activity via Method..
holder.iconslogo.setOnClickListener {
when (charItemlogo.itemsidlogo) {
1 -> {
var btmp= arrayList[0].iconslogo
(context as MakeStylishActivity).setLogos(btmp)
}
Here the Frame is Coming from Frame Fragment to Main Activity
holder.iconsframe.setOnClickListener {
when (charItemFrame.itemsidframe) {
1 -> {
var btmp= arrayList[0].iconsframe
(context as MakeStylishActivity).setFrames(btmp)
}}
This is Main Activity that is Setting the Logo and Frame to Bitmap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_make_stylish)
val byteArray = intent.getByteArrayExtra("pictures")
bmp = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)!!
img_bitmap1.setImageBitmap(bmp)
stringqrcontent= intent.getStringExtra("qrcontent")
bottom_nav_viewstyle.setOnNavigationItemSelectedListener {
when (it.itemId) {
R.id.action_default -> {
true
}
R.id.action_colors -> {
ShowFColorFragment()
true
}
R.id.action_logos -> {
ShowLogoFragment()
true
}
R.id.action_frames -> {
FunctionAddFrames();
true
}
R.id.action_patterns -> {
true
}
else -> false
}
}
}
fun setLogos(btmp: Bitmap?) {
//img_bitmap1.setImageBitmap(btmp)
PutLogoOnQRBitmaps(btmp, bmp!!)
}
fun setFrames(btmp: Bitmap?) {
// img_bitmap1.setImageBitmap(btmp)
//addWhiteBorder(bmp!!,10)
PutFrameImages(btmp, bmp!!)
}
//Combine Frame Behind QR Code
fun PutFrameImages(frame: Bitmap?, image: Bitmap): Bitmap? {
var cs: Bitmap? = null
var rs: Bitmap? = null
rs = Bitmap.createScaledBitmap(frame!!, image.width, image.height, true)
cs = Bitmap.createBitmap(rs.width, rs.height, Bitmap.Config.RGB_565)
val comboImage = Canvas(cs)
comboImage.drawBitmap(image, 0F, 0F, null)
comboImage.drawBitmap(rs, 0F, 0F, null)
if (rs != null) {
rs.recycle()
rs = null
}
// Runtime.getRuntime().gc()
img_bitmap1.setImageBitmap(cs!!)
return cs
}
//Put Logo on QR Code
fun PutLogoOnQRBitmaps(logo: Bitmap?, qrcode: Bitmap): Bitmap? {
val combined = Bitmap.createBitmap(qrcode.width, qrcode.height, qrcode.config)
val canvas = Canvas(combined)
val canvasWidth = canvas.width
val canvasHeight = canvas.height
canvas.drawBitmap(qrcode, Matrix(), null)
val resizeLogo = Bitmap.createScaledBitmap(logo!!, canvasWidth / 5, canvasHeight / 5, true)
val centreX = (canvasWidth - resizeLogo.width) / 2
val centreY = (canvasHeight - resizeLogo.height) / 2
canvas.drawBitmap(resizeLogo, centreX.toFloat(), centreY.toFloat(), null)
img_bitmap1.setImageBitmap(combined)
return combined
}}
I see a few things here that aren't considered a good idea, but the most important is that the size of an Intent has a limit (very small, that is) and is not designed to pass large amounts of data.
What I would do
Regardless of your simplistic Architecture (no usage of ViewModels, or correct separation of concerns, and a few other SOLID principles ignored here...), I would not pass the image via intent. Instead, I would save the image to the filesystem (temporarily), pass the "path" as a string to the next activity, and have said activity open the file and create the Bitmap from the filesystem.
This means you no longer need to worry about going overboard with the image size/intent size, and that your two activities decouple a little bit. You can now pass any path to a bitmap there and the other activity will pick it up, regardless of where it came from.
A second improvement would be to delegate all this (image storing, passing, retrieving, etc.) to a ViewModel + UseCase (and/or Repository), in which case you'd be further decoupling your code. For this, and much more, the starting point would be getting started with Android Jetpack; I recommend at least trying to leverage a ViewModel in your architecture.
You'd also want to be careful when creating bitmaps out of nowhere, you can easily run out of memory by doing what you're doing; you should take a look at Android's official documentation about handling large bitmaps.
I want to show a modal dialogue for the user to select a Bluetooth device in the case that I can't guess it from the device names.
It appears that AlertDialogue has the facility to show a spinner / dropdown.
The alert dialogue builder has a method setItems which I seem to think is what I need, however, its parameter is CharSequence[] but I have some sort of array of strings (I can't tell exactly what I have because everything is just val).
private fun showDialog() {
val names = (bta!!.bondedDevices).map { z -> z.name };
// What is the type of names? How can you find this out?
// How can you make it into a CharSequence[]?
val ab = AlertDialog.Builder(this);
ab.setTitle("Select device");
ab.setIcon(android.R.drawable.ic_dialog_alert); // I'd prefer a question mark.
ab.setPositiveButton("Select"){dialogueInterface, which -> Toast.makeText(applicationContext, "Selected", Toast.LENGTH_LONG).show()};
ab.setNeutralButton("Cancel"){dialogueInterface, which -> Toast.makeText(applicationContext, "Cancelled", Toast.LENGTH_LONG).show()};
ab.setItems(names); // None of the following functions can be called with the arguments supplied.
val a = ab.create();
a.setCancelable(false);
a.show();
}
I think this works in Java, but it doesn't in kotlin
CharSequence[] cs = list.toArray(new CharSequence[list.size()]);
So:
In AndroidStudio how can you tell the type of a variable? (In VisualStudio if you hover over a var then the tooltip tells you.)
How in kotlin do you make a CharSequence[]?
fun elmFind() {
// Find device
val pairedDevices: Set<BluetoothDevice>? = bta!!.bondedDevices
// Try to guess.
for (device in pairedDevices!!) {
if (device.name.contains("obd")) {
elmConnect(device.name);
return;
}
}
// Still going therefore didn't find one therefore ask.
val cs: Array<CharSequence> = pairedDevices.map { z -> z.name }.toTypedArray()
var elmDeviceName: String = ""
val ab = AlertDialog.Builder(this);
ab.setTitle("Select device");
ab.setIcon(android.R.drawable.ic_dialog_alert);
ab.setPositiveButton("Select") { dialogueInterface, which ->
elmConnect(elmDeviceName);
};
ab.setNeutralButton("Cancel") { dialogueInterface, which ->
Toast.makeText(
applicationContext,
"Cancelled",
Toast.LENGTH_LONG
).show()
};
ab.setItems(cs) { dialog, which -> elmDeviceName = cs[which].toString() };
val a = ab.create();
a.setCancelable(false);
a.show();
}
Will this work with the local variable elmDeviceName being used to get the value of the selected item in the case when the OK button is pressed?
I have read the artical. https://proandroiddev.com/android-camerax-tap-to-focus-pinch-to-zoom-zoom-slider-eb88f3aa6fc6
I add Code A to the office camerx sample. https://github.com/android/camera-samples/tree/master/CameraXBasic
The sample code use camerax "1.0.0-alpha06"
I get the error Unresolved reference for zoomRatio, you can see Image 1, how can I fix it?
Code A
private fun setUpPinchToZoom() {
val cameraControl = CameraX.getCameraControl(lensFacing)
val cameraInfo = CameraX.getCameraInfo(lensFacing)
val listener = object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
override fun onScale(detector: ScaleGestureDetector): Boolean {
val currentZoomRatio: Float = cameraInfo.zoomRatio.value ?: 0F
val delta = detector.scaleFactor
cameraControl.setZoomRatio(currentZoomRatio * delta)
return true
}
}
val scaleGestureDetector = ScaleGestureDetector(context, listener)
viewFinder.setOnTouchListener { _, event ->
scaleGestureDetector.onTouchEvent(event)
return#setOnTouchListener true
}
}
Image 1
Zoom controls have been added on version 1.0.0-alpha07. You need to update your code to use at least camera-Core version 1.0.0-alpha07: https://developer.android.com/jetpack/androidx/releases/camera
My application works portait, ma i want fullscreen video playback even in landscape mode using the plugin mentionend above.
For this purpose I create a customrenderer to take access to native AVPlayerViewController Ios Control.
I tried in many many ways, but seems to be impossible to handle exit fullscreen event. In that method i want to force layout portrait. I have the code for reset orientation already implemented but the problem is to put the code in the right place.
Any other that faced the same issue??
I tried to search for something useful in AVPlayerView(not accessible), AVPlayerVideoController, AVPlayerCurrentItem etc
Any ideas?
Thanks you in advance.
I have translated the OC code to C# in this link for you, see the following codes:
using Foundation;
using CoreGraphics;
playerViewController = new AVPlayerViewController();
playerViewController.ContentOverlayView.AddObserver(this, new NSString("bounds"), NSKeyValueObservingOptions.New | NSKeyValueObservingOptions.Old , IntPtr.Zero);
public override void ObserveValue(NSString keyPath, NSObject ofObject, NSDictionary change, IntPtr context)
{
base.ObserveValue(keyPath, ofObject, change, context);
if(ofObject == playerViewController.ContentOverlayView)
{
if(keyPath == "bounds")
{
NSValue oldRect = change.ValueForKey(new NSString("NSKeyValueChangeOldKey")) as NSValue;
NSValue newRect = change.ValueForKey(new NSString("NSKeyValueChangeNewKey")) as NSValue;
CGRect oldBounds = oldRect.CGRectValue;
CGRect newBounds = newRect.CGRectValue;
bool wasFullscreen = CGRect.Equals(oldBounds, UIScreen.MainScreen.Bounds);
bool isFullscreen = CGRect.Equals(newBounds, UIScreen.MainScreen.Bounds);
if(isFullscreen && !wasFullscreen)
{
if(CGRect.Equals(oldBounds,new CGRect(0,0,newBounds.Size.Height, newBounds.Size.Width)))
{
Console.WriteLine("rotated fullscreen");
}
else
{
Console.WriteLine("entered fullscreen");
}
}
else if(!isFullscreen && wasFullscreen)
{
Console.WriteLine("exited fullscreen");
}
}
}
}
I have a myNode: scalafx.scene.Node that I'd like to turn into a bitmap so that I can save it to disk, compare it to other bitmaps, etc. I think the correct code to turn it into a scalafx.scene.image.WritableImage is
val writableImg = new Scene { root = new Group(myNode) }.snapshot(null)
and then I should be able to turn that into a java.awt.image.RenderedImage with
val renderedImg = SwingFXUtils.fromFXImage(writableImg, null)
The problem, as you've probably realized, is that I have to run the code to get wrImg on the ScalaFX thread. There's a question here that explains how to return a value, but I'm not having any luck translating that to Scala. I tried this:
lazy val writableImg: WritableImage = {
val wrImg = new FutureTask(new Callable[WritableImage]() {
override def call(): WritableImage = {
new Scene { root = new Group(myNode) }.snapshot(null)
}
})
Platform.runLater(wrImg)
wrImg.get()
}
but the code just hangs and never completes. Can anyone provide an idiomatic Scala version and/or tell me why the code never returns?
If you just want to save the image to disk you can simply do it on the same thread avoiding complication passing the image around. Something like this will work:
Platform.runLater {
val node = new Circle {
centerX = 200
centerY = 200
radius = 50
stroke = Color.BROWN
strokeWidth = 2
fill = Color.DARKKHAKI
}
val jfxImage = node.snapshot(new SnapshotParameters(), null)
val bufferedImage = SwingFXUtils.fromFXImage(jfxImage, null)
val file = new File("./snapshot.png")
println("Writing snapshot to: " + file.getAbsolutePath)
javax.imageio.ImageIO.write(bufferedImage, "png", file)
()
}
The empty () to have closure returning Unit, so ScalaFX Platform.runLater is happy.
Update:
If you want to have a value from Platform.runLater the approach you suggested in your question should be in general fine. However, you want to make sure that you do not block the FX Application Thread. If you call Platform.runLater on FX Application Thread you will lock out, so you may want to have something like this
def makeSnapshot() = myNode.snapshot(new SnapshotParameters(), null)
val writableImg = if (Platform.isFxApplicationThread) {
makeSnapshot()
} else {
val futureTask = new FutureTask(new Callable[WritableImage]() {
override def call(): WritableImage = makeSnapshot()
})
Platform.runLater(futureTask)
futureTask.get()
}