Right now i'm using the code below in my main activity
imageAnim.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
float x = imageAnim.getX(); // width - activity's field
Log.d("works", "" + x); //return right value
}
});
But I want to move it to a class, but I keep getting errors like cannot resolve symbol getViewTreeObserver. Is there a way to fix this?
You can pass your imageAnim to you class as a parameter passable by the constructor
for example, create a class named ClassHelper as the following :
public class ClassHelper {
private ImageView imageView;
private Context context;
private float x;
ClassHelper(ImageView imageView, Context context) {
this.context = context;
this.imageView = imageView;
}
void setViewTreeObserver() {
imageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
x = imageView.getX();
}
});
}
public float getX() {
return x;
}
}
and call it in like this
ClassHelper classHelper = new ClassHelper(imageAnim,getApplicationContext());
classHelper.getViewTreeObserver();
//getX() method will return the value of X
Toast.makeText(this, "" + classHelper.getX(), Toast.LENGTH_SHORT).show();
Related
I am trying to show a Banner Ad from native java code to Flutter. I've tried seeing multiple plugins to write my code however when my app debugs it always shows me the error - Unhandled Exception: PlatformException(error, Cannot add a null child view to a ViewGroup, null, java.lang.IllegalArgumentException: Cannot add a null child view to a ViewGroup
Here's my Java code using PlatformViewFactory -
public class BannerFactory extends PlatformViewFactory {
private final BinaryMessenger messenger;
public BannerFactory(BinaryMessenger messenger) {
super(StandardMessageCodec.INSTANCE);
this.messenger = messenger;
}
#Override
public PlatformView create(Context context, int viewId, Object args) {
return new BannerUtil(context, this.messenger, viewId, (HashMap) args);
}
}
This is my actual BannerUtil class that contains the callbacks for my Banner Ad.
public class BannerUtil implements PlatformView {
// private FrameLayout layout;
private static final String TAG = "BannerUtil";
private int height;
private int width;
private MethodChannel channel;
private int size;
private String adUnitId;
private String placementId;
private boolean closeButton;
private int refreshTime;
private MBBannerView mtgBannerView;
public BannerUtil(Context context, BinaryMessenger messenger, int id, HashMap args) {
try {
this.size = (int) args.get("size");
this.adUnitId = (String) args.get("adUnitId");
this.placementId = (String) args.get("placementId");
this.closeButton = (boolean) args.get("closeButton");
this.refreshTime = (int) args.get("refreshTime");
this.height = (int) args.get("height");
this.width = (int) args.get("width");
this.channel = new MethodChannel(messenger, "flutter_mintegral");
// this.layout = new FrameLayout(FlutterMintegralPlugin.activity);
mtgBannerView = new MBBannerView(FlutterMintegralPlugin.activity);
mtgBannerView.init(new BannerSize(BannerSize.DEV_SET_TYPE, width, height), placementId, adUnitId);
mtgBannerView.setAllowShowCloseBtn(closeButton);
mtgBannerView.setRefreshTime(refreshTime);
mtgBannerView.setBannerAdListener(new BannerAdListener() {
#Override
public void onLoadFailed(String msg) {
Log.e(TAG, "on load failed" + msg);
}
#Override
public void onLoadSuccessed() {
Log.e(TAG, "on load successed");
}
});
if (mtgBannerView != null) {
// this.layout.removeAllViews();
mtgBannerView.load();
// FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
// FrameLayout.LayoutParams.MATCH_PARENT);
// this.layout.addView(mtgBannerView,0,layoutParams);
// layout.setVisibility(View.VISIBLE);
}
} catch (Exception e) {
Log.e("MintegralSDK", e.toString());
}
}
#Override
public View getView() {
return mtgBannerView;
}
#Override
public void dispose() {
//this.layout.removeAllViews();
if (mtgBannerView != null) {
mtgBannerView.release();
}
}
}
I need help as i'm unsure how to call FrameLayout from Java to call the banner ad in Flutter.
I am even registering my banner ad in my plugins main class like this -
private void RegistrarBanner(PlatformViewRegistry registry, BinaryMessenger messenger) {
registry.registerViewFactory("/Banner", new BannerFactory(messenger));
}
public static void registerWith(Registrar registrar) {
registrar.platformViewRegistry().registerViewFactory("/Banner", new
BannerFactory(registrar.messenger()));
}
#Override
public void onAttachedToEngine(#NonNull FlutterPluginBinding flutterPluginBinding) {
this.flutterPluginBinding = flutterPluginBinding;
this.RegistrarBanner(flutterPluginBinding.getPlatformViewRegistry(), flutterPluginBinding.getBinaryMessenger());
}
This is my Dart part of code (of my internal plugin) which calls AndroidView -
Container(
width: this.sizes[this.widget.size].width,
height: this.sizes[this.widget.size].height,
child: AndroidView(
viewType: '/Banner',
key: UniqueKey(),
creationParams: {
'closeButton': widget.closeButton,
'refreshTime': widget.refreshTime,
'adUnitId': widget.adUnitId,
'placementId': widget.placementId,
'size': this.sizes[this.widget.size].type,
'height': this.sizes[this.widget.size].height,
'width': this.sizes[this.widget.size].width
},
creationParamsCodec: StandardMessageCodec(),
),
);
I'm unsure as to where I'm going wrong? Can someone please help me.
I've been trying to find solution of this issue, but I haven't. The question is to return to Activity from View.
As soon as if statement become false, it should return me to SplashActivity
Here some Activities and View class, that perform all logic of application.
public class MainActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
GameEngine gameEngine = new GameEngine(this);
setContentView(gameEngine);
}
}
Then goes my SplashActivity with one ImageButton which call MainActivity as soon as it's pressed.
public class SplashActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
}
//this method mentioned (onClick) in XML file I've not included.
public void startGame(View view)
{
Intent mainIntent = new Intent(SplashActivity.this, MainActivity.class);
startActivity(mainIntent);
finish();
}
}
My View Class:
public class GameEngine extends View
{
boolean inGame;
//Here some code...
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
if(inGame)
{
canvas.drawBitmap(someImage, positionX, positionY, null);
if(positionX > 100)
{
inGame = false;
//Here I want to return to SplashActivity, where my ImageButton is!
}
}
}
}
I don't know how to implement idea of returning to Activity and begin using my app from innitial point.
Thanks in advance!
EDIT
This information is beside the point!
Innitialy I changed code for better readability to touch on the main point.
This is inner class where it's updating:
public static class Drawable
{
private int x;
private int y;
Drawable(int x, int y)
{
this.x = x;
this.y = y;
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
void update() {
x -= 3;
}
}
This is how myobjects actually draw
for (Drawable drawable : drawables)
{
//Draw upper pipe
canvas.drawBitmap(pipeUp, drawable.getX(), drawable.getY(), null);
//Draw lower pipe
canvas.drawBitmap(pipeBot, drawable.getX(), drawable.getY() +
pipeUp.getHeight() + GAP, null);
}
This is how it updates
private void animation()
{
for(Drawable drawable : new ArrayList<>(drawables))
{
drawable.update();
if(drawable.getX() == dWidth/3 + dWidth/3)
{
drawables.add(new Drawable(pipeX, (int)(Math.random() *
(pipeUp.getHeight() -((pipeUp.getHeight() * 0.25))) - (pipeUp.getHeight() - (pipeUp.getHeight() * 0.25)))));
}
if(drawable.getX() <= 0 - pipeBot.getWidth())
{
drawables.remove(drawable);
}
}
}
Then I call animation() in onDraw method.
You can try:
Intent mainIntent = new Intent(getContext(), SplashActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
getContext().startActivity(mainIntent);
or you can save reference of MainActivity in GameEngine's constructor and use it to open splash:
GameEngine gameEngine = new GameEngine(this);
or you can implement a livedata with a basic repository pattern:
public class DataRepository {
public static MutableLiveData<Boolean> openSplash = new MutableLiveData<>();
}
On MainActivity:
DataRepository.openSplash.observe(this, aBoolean -> {
if(aBoolean){
//open splash
}else{
//
}
});
On GameEngine:
DataRepository.openSplash.postValue(true);
In Service class :
public void onNotify(TransferHandler<ProcessHolder> handler, int percentage) {
updateprocess(percentage);
}
in adapter
onBindViewHolder
progressBar = new ProgressBar(getContext());
progressBar = parentView.findViewById(R.id.progressbar_process);
now I want to access percentage from service class method to this adapter progress
Use EventBus library
After config that follow this steps
1 : Create servieClass.java
public class serviceClass {
private int percentage;
public serviceClass(int percentage) {
this.percentage = percentage;
}
public int getPercentage() {
return percentage;
}
}
2 : Change service
public void onNotify(TransferHandler<ProcessHolder> handler, int percentage) {
updateprocess(percentage);
EventBus.getDefault().post(new servieClass(percentage));
}
3 : add setPercentage function to your Adapter
public void setPercentage(int percentage){
this.percentage = percentage;
notifyDataSetChanged();
}
4 : Finally add this in fragment that you config EventBus in it
#Subscribe
public void onEvent(BlockedEvent event) {
adapter.setPercentage(percentage);
}
Good luck
In the Service class I wrote this
public void onNotify(TransferHandler<ProcessHolder> handler, int percentage) {
Intent intent = new Intent("PercentageUpdates");
intent.putExtra("percentage", percentage);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
and at the Activity side you have to receive this Broadcast message
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
mMessageReceiver, new IntentFilter("PercentageUpdates"));
By this way you can send percentage to an Activity. here mPercentageReceiver is the class in that class you will perform what ever you want....
private BroadcastReceiver mPercentageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String percentage = intent.getStringExtra("percentage");
if (percentage != null) {
// You can set the percentage here
}
}
};
I'm trying to change attribute to custom LinearLayout class, I set the option to class with:
MyBuilder option = new MyBuilder.Builder()
.image(...)
.setCardRadius(...)
.build());
Than i call in MainActivity
MyObject obj = (MyObject) findViewById(R.id.myObject);
obj.init(context, option);
But if I call multiple times obj.init(...) with different option the builder has old value setted so the view cannot change attribute correctly.
So my question is: can I reset Builder o LinearLayout before initializate new object?
This is my LinearLayout.java:
public class MyObject extends LinearLayout{
CardView card;
ImageView image;
float cardRadiusAttr;
View rootView;
AttributeSet attributeSet;
public void init(final Context context, final MyBuilder option){
if(option != null)
{
/*
Get attribute from XML
*/
TypedArray ta = context.obtainStyledAttributes(attributeSet, R.styleable.Card, 0, 0);
try {
cardRadiusAttr = ta.getDimension(R.styleable.Card_c_cardRadius, option.getCardRadius());
} finally {
ta.recycle();
}
/*
Set up xml object.
*/
card = (CardView) findViewById(R.id.card);
image = (ImageView) findViewById(R.id.image);
card.setRadius(cardRadiusAttr);
/**
* Check if Option is set
*/
if (option.isImage() != null) {
//Set Image
}
}else{
Log.e("Initialization", "Option View not initialize!");
}
}
public MyObject(Context context, AttributeSet attrs) {
super(context, attrs);
/*
Inflater custom layout to view.
*/
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
rootView = inflater.inflate(R.layout.Card, this, true);
attributeSet = attrs;
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
}
}
This is MyBuilder.java
public class MyBuilder {
private int mImage;
private float mCardRadius = 4f;
private MyBuilder(Builder builder)
{
mImage = builder.mImage;
mCardRadius = builder.mCardRadius;
}
public static class Builder{
private int mImage;
private float mCardRadius = 4f;
public Builder setCardRadius(float radius)
{
if(radius <= 0)
{
Log.e("CardRadius", "Impossible to set Card Radius lower than 0! Please Check it");
}
else {
mCardRadius = radius;
}
return this;
}
public Builder image(int image) {
if(image == 0)
{
Log.e("Image", "Impossible to set Image to 0! Please Check it");
}
else {
mImage = image;
}
return this;
}
public MyBuilder build() {
return new MyBuilder(this);
}
}
public int getImage() {
return mImage;
}
public float getCardRadius() {
return mCardRadius;
}
}
I finally found the issue.
In the init method of the MyObject you have to clean up the View after the previous use.
In this particular case, first, you pass one set of options. Based on them, View is adjusting Visibility of its controls (making button1, button2, etc. Visible). But when you pass another set of options - you have to erase all changes have been made before. (i.e. hide button1, button2, etc. and let the View to adjust Visibility of its controls once again)
I am trying to create an image viewer which can load images from given URLs.The code below implement the User Interface.
My intention is to let user Zoom the image and move to next image with a Swipe event.But the problem is that when i zoom and then swipe , instead of showing the remaining portion , it moves to the next image.
I tried using requestDisallowInterceptTouchEvent in TouchImageView's(https://github.com/MikeOrtiz/TouchImageView) onTouchListener .After this the remaining portion could show but now i cannot go to the next page. I was wondering how this can be achieved as the event can only go to either TouchView or PageAdapter
public class PageActivity extends Activity {
private int numPages = 33;
private TouchImageView[] imageViews = new TouchImageView[numPages];
private String URL = "http://www.smbc-comics.com/comics/200905";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ViewPager viewPager = new ViewPager(this);
for (int i = 0; i < numPages; i++) {
imageViews[i] = new TouchImageView(this);
imageViews[i].setBackgroundResource(R.drawable.banke);
imageViews[i].setMaxZoom(4f);
}
setContentView(viewPager);
ImagePagerAdapter adapter = new ImagePagerAdapter();
viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(2);
}
#SuppressWarnings("unused")
private class ImagePagerAdapter extends PagerAdapter {
#Override
public int getCount() {
return numPages;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == ((TouchImageView) object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Context context = PageActivity.this;
String pageURL = URL;
if (imageViews[position].getDrawable() == null) {
ImageFetcher imagefetcher = new ImageFetcher();
imagefetcher.execute(
pageURL + String.format("%02d", position+1) + ".gif",
String.valueOf(position));
}
((ViewPager) container).addView(imageViews[position], 0);
return imageViews[position];
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
((ViewPager) container).removeView((TouchImageView) object);
imageViews[position].setImageDrawable(null);
}
}
public class ImageFetcher extends AsyncTask<String, Integer, Drawable> {
int fillthisPos;
public Drawable doInBackground(String... urls) {
try {
InputStream is = (InputStream) new URL(urls[0]).getContent();
fillthisPos = Integer.parseInt(urls[1]);
Drawable d = Drawable.createFromStream(is, "src name");
return d;
} catch (Exception e) {
return null;
}
}
#Override
protected void onPostExecute(Drawable result) {
super.onPostExecute(result);
imageViews[fillthisPos].setImageDrawable(result);
result = null;
}
}
}
You can add following code in TouchImageView class:
public boolean isZoomed () {
return (normalizedScale > minScale);
}
private void onSwipeEvent(MotionEvent event) {
boolean zoomed = this.isZoomed();
if (!zoomed && (swipeLen > 0)) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
swipeStartPos = (int) event.getRawX();
}
else if (event.getAction() == MotionEvent.ACTION_MOVE) {
int distance = ((int) event.getRawX()) - swipeStartPos;
int swipeVector = SWIPE_RIGHT;
if (distance < 0) swipeVector = SWIPE_LEFT;
if (Math.abs(distance) > swipeLen) {
onSwipeHandler.onSwipe(swipeVector);
swipeStartPos = (int) event.getRawX();
this.setScaleX(1f);
}
else {
int swipeStDist = swipeLen - Math.round(((swipeLen / 100) * 50));
if (Math.abs(distance) > swipeStDist) {
this.setScaleX(0.98f);
}
}
}
else if (event.getAction() == MotionEvent.ACTION_UP) {
swipeStartPos = (int) event.getRawX();
this.setScaleX(1f);
}
}
}
public static int SWIPE_LEFT = 0;
public static int SWIPE_RIGHT = 1;
public int swipeStartPos = 0;
public onSwipeListener onSwipeHandler = new onSwipeListener() {
public void onSwipe(int vector) {}
};
public static int swipeLen = 0;
public void setOnSwipeListener(onSwipeListener c, int swipeLength) {
onSwipeHandler = c;
swipeLen = swipeLength;
}
public interface onSwipeListener {
public void onSwipe(int vector);
}
And also in TouchImageView below line 636 add onSwipeEvent(event) like this:
......
setImageMatrix(matrix);
onSwipeEvent(event);
//
// indicate event was handled
//
return true;
...........
After this from you code you can add swipe event listener, like this:
imageView.setOnSwipeListener(new TouchImageView.onSwipeListener() {
#Override
public void onSwipe(int vector) {
if (vector == TouchImageView.SWIPE_LEFT) {
Log.d("swipe", "swipe left!");
}
else if (vector == TouchImageView.SWIPE_RIGHT) {
Log.d("swipe", "swipe right!");
}
}
}, 200); //length of swiping - 200 dip
This onSwipeListener ignore onswipe where image is zoomed+. It work for me.