WindowManager更新内容会导致视图堆叠

时间:2014-03-13 18:45:00

标签: java android service window-managers

编辑: 更奇怪。将像素格式设置为半透明而不是不透明似乎已修复它,我至少无法看到“堆叠”数字。

非常奇怪的行为。

我正在使用服务来绘制system_overlay视图。视图添加并显示就好了。

这个视图是一个倒数计时器,所以我需要每秒更新一次文本。我使用一个调用postDelayed的处理程序来处理它,并从处理程序执行的runnable中调用textView.setText("CONTENT")

这是奇怪的地方。

文字更新,但似乎是堆叠。我在00:01下看到00:00等等。每个滴答都会导致另一层。

我已在标准活动中测试了此代码和视图,文本呈现完美,没有“堆栈”。直到通过WindowManager添加它才会导致行为出现问题。

TimerView.java

    /*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.t3hh4xx0r.lifelock.widgets;

import java.util.concurrent.TimeUnit;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PointF;
import android.os.Handler;
import android.os.IBinder;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.t3hh4xx0r.lifelock.R;
import com.t3hh4xx0r.lifelock.services.TimerDrawerService;

/**
 * View used to draw a running timer.
 */
public class TimerView extends FrameLayout {
    int alpha = 100;
    TimerDrawerService.ServiceBinder drawerBinder;

    PointF firstFinger;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            firstFinger = new PointF(event.getX(), event.getY());
            break;

        case MotionEvent.ACTION_MOVE:
            PointF newFinger = new PointF(event.getX(), event.getY());
            float distance = newFinger.x - firstFinger.x;
            float part = Math.abs(distance);
            float percentOfMaxTraveled = (part * 100) / getWidth();
            int nextAlpha = 100 - Float.valueOf(percentOfMaxTraveled).intValue();
            if (nextAlpha < 20) {
                Toast.makeText(getContext(), "Dismissed", Toast.LENGTH_LONG).show();
                if (drawerBinder != null) {
                    drawerBinder.remove();
                }
                return true;
            }
            if (nextAlpha < alpha) {
                alpha = nextAlpha;
            }
            Log.d("THE PERCENT TRAVELED", String.valueOf(percentOfMaxTraveled) + " : " + String.valueOf(alpha));
            this.invalidate();
            break;
        }

        return true;
    }

    @Override
    public void onDraw(Canvas canvas) {
        canvas.saveLayerAlpha(0, 0, canvas.getWidth(), canvas.getHeight(),
                alpha, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
        super.onDraw(canvas);
    }

    /**
     * Interface to listen for changes on the view layout.
     */
    public interface ChangeListener {
        /** Notified of a change in the view. */
        public void onChange();
    }

    private static final long DELAY_MILLIS = 1000;

    private final TextView mMinutesView;
    private final TextView mSecondsView;

    private final int mWhiteColor;
    private final int mRedColor;

    private final Handler mHandler = new Handler();
    private final Runnable mUpdateTextRunnable = new Runnable() {
        @Override
        public void run() {
            if (mRunning) {
                mHandler.postDelayed(mUpdateTextRunnable, DELAY_MILLIS);
                updateText();
            }
        }
    };

    private final Timer mTimer;
    private final Timer.TimerListener mTimerListener = new Timer.TimerListener() {
        @Override
        public void onStart() {
            mRunning = true;
            long delayMillis = Math.abs(mTimer.getRemainingTimeMillis())
                    % DELAY_MILLIS;
            if (delayMillis == 0) {
                delayMillis = DELAY_MILLIS;
            }
            mHandler.postDelayed(mUpdateTextRunnable, delayMillis);
        }
    };

    private boolean mRunning;
    private boolean mRedText;

    private ChangeListener mChangeListener;

    public TimerView(Context context) {
        this(context, null, 0);
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            if (service instanceof TimerDrawerService.ServiceBinder) {
                drawerBinder = (com.t3hh4xx0r.lifelock.services.TimerDrawerService.ServiceBinder) service;
            }
            // No need to keep the service bound.
            getContext().unbindService(this);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // Nothing to do here.
        }
    };

    public TimerView(Context context, AttributeSet attrs, int style) {
        super(context, attrs, style);
        context.bindService(new Intent(context, TimerDrawerService.class), mConnection, 0);

        LayoutInflater.from(context).inflate(R.layout.timer, this);

        mMinutesView = (TextView) findViewById(R.id.minutes);
        mSecondsView = (TextView) findViewById(R.id.seconds);

        mWhiteColor = context.getResources().getColor(android.R.color.white);
        mRedColor = Color.RED;

        mTimer = new Timer();
        mTimer.setListener(mTimerListener);
        mTimer.setDurationMillis(0);

    }

    public Timer getTimer() {
        return mTimer;
    }

    /**
     * Set a {@link ChangeListener}.
     */
    public void setListener(ChangeListener listener) {
        mChangeListener = listener;
    }

    /**
     * Updates the text from the Timer's value.
     */
    private void updateText() {
        long remainingTimeMillis = mTimer.getRemainingTimeMillis();

        if (remainingTimeMillis > 0) {
            mRedText = false;
            // Round up: x001 to (x + 1)000 milliseconds should resolve to x
            // seconds.
            remainingTimeMillis -= 1;
            remainingTimeMillis += TimeUnit.SECONDS.toMillis(1);
        } else {
            mRedText = !mRedText;
            remainingTimeMillis = Math.abs(remainingTimeMillis);
        }

        if (mRedText) {
            // Sync the sound with the red text.
        }

        updateText(remainingTimeMillis, mRedText ? mRedColor : mWhiteColor);
    }

    /**
     * Updates the displayed text with the provided values.
     */
    private void updateText(long timeMillis, int textColor) {
        timeMillis %= TimeUnit.HOURS.toMillis(1);
        mMinutesView.setText(String.format("%02d",
                TimeUnit.MILLISECONDS.toMinutes(timeMillis)));
        mMinutesView.setTextColor(textColor);
        timeMillis %= TimeUnit.MINUTES.toMillis(1);
        mSecondsView.setText(String.format("%02d",
                TimeUnit.MILLISECONDS.toSeconds(timeMillis)));
        mSecondsView.setTextColor(textColor);
        if (mChangeListener != null) {
            mChangeListener.onChange();
        }

    }

    public void showMessage(boolean didGood) {
        // mTipView.setText((didGood ? "Good" : "Bad") + " job!");
    }

    public void setLocked(boolean b) {
        if (b) {
            ((WindowManager.LayoutParams) getLayoutParams()).type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
        } else {
            ((WindowManager.LayoutParams) getLayoutParams()).type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
        }
    }
}

TimerDrawerService

    package com.t3hh4xx0r.lifelock.services;

/*
 Copyright 2011 jawsware international

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import android.view.Gravity;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;

import com.t3hh4xx0r.lifelock.objects.Peek;
import com.t3hh4xx0r.lifelock.widgets.TimerView;

public class TimerDrawerService extends Service {
    TimerView root;
    Peek currentInstance;

    private ServiceBinder mBinder = new ServiceBinder();

    public class ServiceBinder extends Binder {
        public TimerView getRoot() {
            return root;
        }

        public void remove() {
            removeViews();
        }

        public void add() {
            addViews();
        }

    }

    static public void start(Context c, Peek currentInstance) {
        Intent i = new Intent(c, TimerDrawerService.class);
        i.putExtra("peek", currentInstance);
        c.startService(i);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("CRATING VIEW HERE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",
                "NOW MAN");

        root = new TimerView(this);
        root.getTimer().setDurationMillis(90 * 1000);
        root.getTimer().start();
        addViews();
    }

    public void addViews() {
        ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).addView(
                root, getLayoutParams());
    }

    public static boolean isRunning(Context c) {
        ActivityManager manager = (ActivityManager) c
                .getSystemService(ACTIVITY_SERVICE);
        for (RunningServiceInfo service : manager
                .getRunningServices(Integer.MAX_VALUE)) {
            if ("com.t3hh4xx0r.lifelock.service.TimerDrawerService"
                    .equals(service.service.getClassName())) {
                return true;
            }
        }
        return false;
    }

    private WindowManager.LayoutParams getLayoutParams() {
        LayoutParams layoutParams = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.MATCH_PARENT,
                WindowManager.LayoutParams.MATCH_PARENT,
                WindowManager.LayoutParams.TYPE_SYSTEM_ERROR, 0,
                PixelFormat.OPAQUE);
        layoutParams.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;

        layoutParams.gravity = Gravity.CENTER;
        return layoutParams;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        currentInstance = (Peek) intent.getSerializableExtra("peek");
        return START_STICKY;
    }

    public void removeViews() {
        ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
                .removeView(root);
    }
}

Stacking 1

Stacking 2

0 个答案:

没有答案