Last active
July 21, 2016 20:21
-
-
Save caseykulm/e146bae972a4e6481ee6fce4c22f7daf to your computer and use it in GitHub Desktop.
FrameLayout that knows when it first becomes visible on screen
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.annotation.TargetApi; | |
import android.content.Context; | |
import android.graphics.Canvas; | |
import android.graphics.Point; | |
import android.graphics.Rect; | |
import android.os.Build; | |
import android.util.AttributeSet; | |
import android.util.Log; | |
import android.view.ViewTreeObserver; | |
import android.widget.FrameLayout; | |
import java.util.HashSet; | |
import java.util.Set; | |
public class EventView extends FrameLayout { | |
private static final String TAG = EventView.class.getSimpleName(); | |
private String eventViewId; | |
public EventView(Context context) { | |
super(context); | |
init(); | |
} | |
public EventView(Context context, AttributeSet attrs) { | |
super(context, attrs); | |
init(); | |
} | |
public EventView(Context context, AttributeSet attrs, int defStyleAttr) { | |
super(context, attrs, defStyleAttr); | |
init(); | |
} | |
@TargetApi(Build.VERSION_CODES.LOLLIPOP) | |
public EventView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { | |
super(context, attrs, defStyleAttr, defStyleRes); | |
init(); | |
} | |
private void init() { | |
setWillNotDraw(false); | |
getViewTreeObserver().addOnGlobalLayoutListener(new EventViewGlobalLayoutListener(this, onScreenListener)); | |
getViewTreeObserver().addOnScrollChangedListener(new EventViewScrollChangeListener(this, onScreenListener)); | |
} | |
private final OnScreenListener onScreenListener = new OnScreenListener() { | |
private Set<String> seenIds = new HashSet<>(); | |
@Override | |
public void onScreen(EventView eventView) { | |
if (!seenIds.contains(eventView.getEventViewId())) { | |
firstSeen(); | |
seenIds.add(eventView.getEventViewId()); | |
} | |
} | |
}; | |
protected void firstSeen() { | |
Log.d(TAG, "onScreen() - eventView: " + getEventViewId()); | |
} | |
@Override | |
protected void onDraw(Canvas canvas) { | |
super.onDraw(canvas); | |
if (eventViewId == null || eventViewId.isEmpty()) { | |
throw new IllegalStateException("eventViewId must be set"); | |
} | |
} | |
public void setEventViewId(String eventViewId) { | |
this.eventViewId = eventViewId; | |
} | |
public String getEventViewId() { | |
return eventViewId; | |
} | |
interface OnScreenListener { | |
void onScreen(EventView eventView); | |
} | |
private static class EventViewSubject { | |
private EventView eventView; | |
private OnScreenListener onScreenListener; | |
private final Rect rect; | |
private final Point globalOffset; | |
EventViewSubject(EventView view, OnScreenListener onScreenListener) { | |
Log.d(TAG, "eventViewSubject() - ev: " + view.hashCode() + ", osl: " + onScreenListener.hashCode()); | |
this.eventView = view; | |
this.onScreenListener = onScreenListener; | |
this.rect = new Rect(); | |
this.globalOffset = new Point(); | |
} | |
public void processEvent() { | |
if (eventViewVisible()) { | |
onScreenListener.onScreen(eventView); | |
} | |
} | |
private boolean eventViewVisible() { | |
if (eventView == null) { | |
return false; | |
} | |
boolean isVisibleOnScreen = eventView.getGlobalVisibleRect(rect, globalOffset); | |
Log.d(TAG, "onListener() - ev " + eventView.getEventViewId() + " data - width: " + eventView.getWidth() + ", height: " + eventView.getHeight() + ", x: " + eventView.getX() + ", y: " + eventView.getY() + ", visibleOnScreen: " + isVisibleOnScreen); | |
return isVisibleOnScreen; | |
} | |
} | |
private static class EventViewScrollChangeListener implements ViewTreeObserver.OnScrollChangedListener { | |
private final EventViewSubject eventViewSubject; | |
EventViewScrollChangeListener(EventView view, OnScreenListener onScreenListener) { | |
this.eventViewSubject = new EventViewSubject(view, onScreenListener); | |
} | |
@Override | |
public void onScrollChanged() { | |
eventViewSubject.processEvent(); | |
} | |
} | |
private static class EventViewGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener { | |
private final EventViewSubject eventViewSubject; | |
EventViewGlobalLayoutListener(EventView view, OnScreenListener onScreenListener) { | |
this.eventViewSubject = new EventViewSubject(view, onScreenListener); | |
} | |
@Override | |
public void onGlobalLayout() { | |
eventViewSubject.processEvent(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment