const visibleIndex = newSet(); //全局创建一个不重复的集合 let ob = newIntersectionObserver((entries) => { for (const e of entries) { const index = e.target.dataset.index; //isIntersecting为true就代表在视口内 if (e.isIntersecting) {
visibleIndex.add(index);
} else {
visibleIndex.delete(index);
}
} debounceLoadPage();// 防抖后的loadpage
});
3.获取集合的最大及最小的索引
functiongetRange() { if (visibleIndex.size === 0) return [0, 0]; const max = Math.max(...visibleIndex); const min = Math.min(...visibleIndex); return [min, max];
}
4.加载视口内的元素的资源
functionloadPage() { // 得到当前能看到的元素索引范围 const [minIndex, maxIndex] = getRange(); const pages = newSet(); // 不重复的页码集合 for (let i = minIndex; i <= maxIndex; i++) {
pages.add(getPage(i, SIZE));// 遍历将侦查器集合范围内的所在页面都加入到pages的集合内
} // 遍历页码集合 for (const page of pages) { const [minIndex, maxIndex] = getIndexRange(page, SIZE);//获取页码的索引范围 if (contain.children[minIndex].dataset.loaded) { //如果页码最小索引的元素有自定义属性就跳过,代表加载过 continue;
}
contain.children[minIndex].dataset.loaded = true;//如果没有就代表没有加载过,添加上自定义属性 //将当前页码传给获取资源的函数 getVideo(page, SIZE).then((res) => { //拿到当前页面需要的数据数组,遍历渲染到页面上 for (let i = minIndex; i < maxIndex; i++) { const item = contain.children[i];
item.innerHTML = `<img src="${res[i - minIndex].cover}" alt="">`;
}
});
}
}
<body> <divclass="contain"></div> <divclass="btn"> <buttonclass="full-rounded"> <span>刚刚看过</span> <divclass="border full-rounded"></div> </button> </div> <scriptsrc="./api.js"></script> <scriptsrc="./index.js"></script> <script> constSIZE = 15; let contain = document.querySelector(".contain"); let btn = document.querySelector(".btn"); // 页码 let i = 1;
const visibleIndex = newSet();
// 视口观察器 let ob = newIntersectionObserver((entries) => { for (const e of entries) { const index = e.target.dataset.index; if (e.isIntersecting) { // 将在视口内的元素添加到集合内
visibleIndex.add(index);
} else { // 将不在视口内的元素从集合内删除
visibleIndex.delete(index);
}
} debounceLoadPage();
});
functiongetRange() { if (visibleIndex.size === 0) return [0, 0]; const max = Math.max(...visibleIndex); const min = Math.min(...visibleIndex); return [min, max];
}
第一次先点击快照记录初始的内存情况,然后我们多次点击按钮后再次点击快照,记录此时的内存情况,发现从原来的1.1M内存空间变成了1.4M内存空间,然后我们选中第二条快照记录,可以看到右上角有个All objects的字段,其表示展示的是当前选中的快照记录所有对象的分配情况,而我们想要知道的是第二条快照与第一条快照的区别在哪,所以选择Object allocated between Snapshot1 and Snapshot2即展示第一条快照和第二条快照存在差异的内存对象分配情况,此时可以看到Array的百分比很高,初步可以判断是该变量存在问题,点击查看详情后就能查看到该变量对应的具体数据了
mSolidPaint = new Paint();
mSolidPaint.setFlags(Paint.ANTI_ALIAS_FLAG); mPorterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN); point = new PointF(0, 0); mTargetZone = 1;
let left = startPos.current.left; let top = startPos.current.top; const width = Math.abs(e.clientX - startPos.current.left); const height = Math.abs(e.clientY - startPos.current.top);
// 当后面位置小于前面位置的时候,需要把框的坐标设置为后面的位置 if (e.clientX < startPos.current.left) {
left = e.clientX;
}
if (e.clientY < startPos.current.top) {
top = e.clientY;
}
/**
* Union type with pipe operator
* @typedef {Date | string | number} MixDate
*/
/**
* @param {MixDate} date
* @returns {void}
*/ functionshowDate(date) { // date is Date if (date instanceofDate) date; // date is string elseif (typeof date === 'string') date; // date is number else date;
}
/**
* Restrict template by types
* @template {string|number|symbol} T
* @templateY
* @param {T} key
* @param {Y} value
* @returns {{ [K in T]: Y }}
* @example signature:
* function toObject<T extends string | number | symbol, Y>(key: T, value: Y): { [K in T]: Y; }
*/ functiontoObject(key, value) { return { [key]: value };
}
类型守卫:
/**
* @param {any} value
* @return {value is YOUR_TYPE}
*/ functionisYourType(value) { let isType; /**
* Do some kind of logical testing here
* - Always return a boolean
*/ return isType;
}
int childCount = mWrapperView.getChildCount();
if (childCount > 2) {
throw new IllegalStateException("SlidingFoldLayout should host only two child");
}
ViewGr0up.LayoutParams lp = child.getLayoutParams();
if (lp != null && lp instanceof LinearLayout.LayoutParams) { lp = new LinearLayout.LayoutParams(lp);
child.setLayoutParams(lp);
}
mWrapperView.addView(child);
}
public int getRealChildCount() {
if (mWrapperView == null) {
return 0;
}
return mWrapperView.getChildCount();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if(isFirstLayout && getRealChildCount()==2){
View leftView = mWrapperView.getChildAt(0);
scrollTo(leftView.getWidth(),0);
} isFirstLayout = true;
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
int realCount = getRealChildCount();
if(realCount!=2) return;
View leftView = mWrapperView.getChildAt(0);
leftView.layout(l,t,l+leftView.getWidth(),t+leftView.getHeight()); maskAlpha = leftView.getLeft()*1.0f/leftView.getWidth();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action){
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_OUTSIDE:
super.onTouchEvent(ev);
scrollToTraget();
break;
}
return super.onTouchEvent(ev);
}
private void scrollToTraget() {
int count = getRealChildCount();
if(count!=2) return;
int with = getWidth();
if(with==0) return;
@Override
public void addView(View child, int index) {
int childCount = mWrapperView.getChildCount();
if (childCount > 2) {
throw new IllegalStateException("SlidingFoldLayout should host only two child");
}
ViewGr0up.LayoutParams lp = child.getLayoutParams();
if (lp != null && lp instanceof LinearLayout.LayoutParams) { lp = new LinearLayout.LayoutParams(lp);
child.setLayoutParams(lp);
}
mWrapperView.addView(child, index);
}
@Override
public void addView(View child, ViewGr0up.LayoutParams params) {
int childCount = mWrapperView.getChildCount();
if (childCount > 2) {
throw new IllegalStateException("SlidingFoldLayout should host only two child");
}
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(params);
child.setLayoutParams(lp);
mWrapperView.addView(child, lp);
}
@Override
public void addView(View child, int index, ViewGr0up.LayoutParams params) {
int childCount = mWrapperView.getChildCount();
if (childCount > 2) {
throw new IllegalStateException("SlidingFoldLayout should host only two child");
}
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(params);
child.setLayoutParams(lp);
mWrapperView.addView(child, index);
}
最后表达一下我对这个 API 看法,这个 API 设计得不是很好,比如应用想知道用户是否截图了,应用可能需要知道的是,截图文件的存放路径,但是 onScreenCaptured 是一个空参函数,也就意味着没有携带任何参数,如果要实现获取截图文件存放路径的需求,可能还需要沿用之前的老方式,即使用 ContentObserver 监听媒体数据库的变化,然后从几个维度(文件时间维度、文件路径维度、图片尺寸维度)判断新增的图片是否为用户的截图,这种实现的方式相对是比较麻烦的,但是也无发现更好的实现方式。
private int getUniqueFakeId(Activity activity, int id) { if(activity==null){ return id;
}
int newId = id; do{
View v = activity.findViewById(id); if(v!=null){
newId += 1; continue;
}
newId = id; break;
}while (true); return newId;
}
public void setFragment(Fragment fragment) { this.fragment = fragment;
}
switch (e.getActionMasked()) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_OUTSIDE:
canHorizontalSlide = false;//不要拦截该类事件 break;
} if (canHorizontalSlide) { returntrue;
} returnfalse;
}