QQ视差特效和ListView侧滑删除
如图所示是效果图,当向下拉时,图片会被拉出来,松手后恢复。和ListView的侧滑删除
1.视差特效
首先图片是通过addHeaderView加上去的,所以在设置Adapter前先设置一个View来作为头布局,图片的设置android:scaleType="centerCrop"
然后可以重写ListView主要是用通过overScrollBy来实现图片的拉出效果的,当拉出图片绘制后,重写onTouchEvent方法MotionEvent.ACTION_UP
实现回弹
如下是主代码
package com.demo.sb.main; import com.demo.sb.adapter.ParallacAdapter;
import com.demo.sb.widget.MyParallaxListView;
import com.demo.suibian.R; import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.ImageView; /**
* 视差特效类型于QQ空间上的
*
* @author Administrator
*
*/
public class MActivity_Parallac extends Activity { private MyParallaxListView mListView; @Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.mactivity_parallax); mListView = (MyParallaxListView) findViewById(R.id.lv);
mListView.setOverScrollMode(View.OVER_SCROLL_NEVER);
// 加上Header
final View mHeaderView = View.inflate(MActivity_Parallac.this,
R.layout.viewheader, null);
final ImageView mImage = (ImageView) mHeaderView.findViewById(R.id.iv);
mListView.addHeaderView(mHeaderView); mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() { @Override
public void onGlobalLayout() {
// TODO Auto-generated method stub
mListView.setParallaxImage(mImage); mHeaderView.getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
}
}); // mListView.setAdapter(new SuibianAdapter(this));
mListView.setAdapter(new ParallacAdapter(this));
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" > <ImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="200dp"
android:contentDescription="@null"
android:scaleType="centerCrop"
android:src="@drawable/d" /> </LinearLayout>
重写的ListView
package com.demo.sb.widget; import com.nineoldandroids.animation.ValueAnimator;
import com.nineoldandroids.animation.ValueAnimator.AnimatorUpdateListener; import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.animation.Animation;
import android.view.animation.OvershootInterpolator;
import android.view.animation.Transformation;
import android.widget.ImageView;
import android.widget.ListView; public class MyParallaxListView extends ListView{ private ImageView mImage;
private int drawableHeight;
private int mOriginalHeight;
public MyParallaxListView(Context context) {
super(context);
// TODO Auto-generated constructor stub
} public MyParallaxListView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
} public MyParallaxListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
} /**
* 设置ImageView图片,拿到引用
* @param mImage
*/
public void setParallaxImage(ImageView mImage) {
// TODO Auto-generated method stub
this.mImage = mImage;
mOriginalHeight = mImage.getHeight();
drawableHeight = mImage.getDrawable().getIntrinsicHeight(); Log.d("jiejie", "height: " + mOriginalHeight + " drawableHeight: " + drawableHeight);
} @Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX,
int scrollY, int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
// TODO Auto-generated method stub
// deltaY : 竖直方向的瞬时偏移量 / 变化量 dx 顶部到头下拉为-, 底部到头上拉为+
// scrollY : 竖直方向的偏移量 / 变化量
// scrollRangeY : 竖直方向滑动的范围
// maxOverScrollY : 竖直方向最大滑动范围
// isTouchEvent : 是否是手指触摸滑动, true为手指, false为惯性 Log.d("jiejie", "deltaY: " +deltaY + " scrollY: " + scrollY + " scrollRangeY: " + scrollRangeY
+ " maxOverScrollY: " + maxOverScrollY + " isTouchEvent: " + isTouchEvent);
//手指拉动并且是下拉
if(isTouchEvent && deltaY <0){
//把拉动的瞬时变化量的绝对值交给Header,就可以实现放大的效果了
if(mImage.getHeight() <=drawableHeight ){
int newHeight = (int)(mImage.getHeight() + Math.abs(deltaY / 3.0f));
//高度不超过图片最大高度时,才让其生效
mImage.getLayoutParams().height = newHeight;
mImage.requestLayout();
}
} return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
} @Override
public boolean onTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
//执行回弹动画,方式一:属性动画
//从当前高度mImage.getHeight(),执行动画到原始高度mOriginalHeight
final int startHeight = mImage.getHeight();
final int endHeight = mOriginalHeight;
//ValueAnimator(startHeight,endHeight); //执行回弹动画,方式二:自定义Animation
ResetAnimation animation = new ResetAnimation(mImage,startHeight,endHeight);
startAnimation(animation); break; default:
break;
}
return super.onTouchEvent(ev);
} private void ValueAnimator(final int startHeight, final int endHeight) {
// TODO Auto-generated method stub
ValueAnimator mValueAnimator = ValueAnimator.ofInt(1);
mValueAnimator.addUpdateListener(new AnimatorUpdateListener() { @Override
public void onAnimationUpdate(ValueAnimator arg0) {
// TODO Auto-generated method stub
float fraction = arg0.getAnimatedFraction();
//percent 0.0-1.0
Integer newHeight = evaluate(fraction, startHeight, endHeight); mImage.getLayoutParams().height = newHeight;
mImage.requestLayout();
}
});
mValueAnimator.setInterpolator(new OvershootInterpolator());
mValueAnimator.setDuration(800);
mValueAnimator.start();
}
/**
* 类型估值器
* @param fraction
* @param startValue
* @param endValue
* @return
*/
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
} /**
* 执行的动画
* @author Administrator
*
*/
private class ResetAnimation extends Animation{ private final ImageView mImageView;
private final int startHeight;
private final int endHeight; public ResetAnimation(ImageView mImage, int startHeight, int endHeight) {
// TODO Auto-generated constructor stub
this.mImageView = mImage;
this.startHeight = startHeight;
this.endHeight = endHeight;
/**
* Interpolator被用来修饰动画效果,定义动画的变化率,可以使存在的动画效果accelerated(加速)
* decelerated(减速),repeated(重复),bounced(弹跳)等
*
* OvershootInterpolator 向前甩一定值后再回到原来位置
*/
setInterpolator(new OvershootInterpolator());
//设置动画的执行时长
setDuration(800);
}
@Override
protected void applyTransformation(float interpolatedTime,
Transformation t) {
// TODO Auto-generated method stub
System.out.println(interpolatedTime );
//interpolatedTime 0.0f -> 1.0f
Integer newHeightInteger = evaluate(interpolatedTime, startHeight, endHeight); mImageView.getLayoutParams().height = newHeightInteger;
mImageView.requestLayout();
super.applyTransformation(interpolatedTime, t);
}
}
}
2.实现ListView的侧滑删除功能
自定义的布局来显示侧滑的功能
package com.demo.sb.adapter; import com.demo.sb.utils.DensityUtil; import android.content.Context;
import android.graphics.Rect;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout; /**
* 侧拉删除控件
*
* @author Administrator
*
*/
public class SwipeLayout extends FrameLayout { private Status status = Status.Close;
private OnSwipeLayoutListener swipeLayoutListener; public Status getStatus() {
return status;
} public void setStatus(Status status) {
this.status = status;
} public OnSwipeLayoutListener getSwipeLayoutListener() {
return swipeLayoutListener;
} public void setSwipeLayoutListener(OnSwipeLayoutListener swipeLayoutListener) {
this.swipeLayoutListener = swipeLayoutListener;
} public static enum Status {
Close, Open, Draging
} public static interface OnSwipeLayoutListener {
void onClose(SwipeLayout mSwipeLayout); void onOpen(SwipeLayout mSwipeLayout); void onDraging(SwipeLayout mSwipeLayout); // 要去关闭
void onStartClose(SwipeLayout mSwipeLayout); // 要去开启
void onStartOpen(SwipeLayout mSwipeLayout);
} public SwipeLayout(Context context) {
this(context, null);
// TODO Auto-generated constructor stub
} public SwipeLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
// TODO Auto-generated constructor stub
} public SwipeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
mDragHelper = ViewDragHelper.create(this, 1.0f, mCallback);
} ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {
// c . 重写监听
@Override
public boolean tryCaptureView(View arg0, int arg1) {
// TODO Auto-generated method stub
return true;
} // 限定移动范围
public int clampViewPositionHorizontal(View child, int left, int dx) {
// left
if (child == mFrontView) {
if (left > 0) {
return 0;
} else if (left < -mRange) {
return -mRange;
}
} else if (child == mBackView) {
if (left > mWidth) {
return mWidth;
} else if (left < mWidth - mRange) {
return mWidth - mRange;
}
}
return left;
}; public void onViewPositionChanged(View changedView, int left, int top,
int dx, int dy) {
// 专递事件
if (changedView == mFrontView) {
mBackView.offsetLeftAndRight(dx);
} else if (changedView == mBackView) {
mFrontView.offsetLeftAndRight(dx);
}
despatchSwipeEvent(); // 兼容老版本
invalidate(); }; public void onViewReleased(View releasedChild, float xvel, float yvel) {
if (xvel == 0 && mFrontView.getLeft() < -mRange / 2.0f) {
open();
} else if (xvel < 0) {
open();
} else {
close();
}
}; }; private ViewDragHelper mDragHelper;
private View mBackView;
private View mFrontView;
private int mHeight;
private int mWidth;
private int mRange; // b. 专递触摸事件
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
return mDragHelper.shouldInterceptTouchEvent(ev);
} protected void despatchSwipeEvent() {
// TODO Auto-generated method stub
if (swipeLayoutListener != null) {
swipeLayoutListener.onDraging(this);
}
// 记录上一次的状态
Status preStatus = status;
// 更新当前的状态
status = updateStatus();
if (preStatus != status && swipeLayoutListener != null) {
if (status == Status.Close) {
swipeLayoutListener.onClose(this);
} else if (status == Status.Open) {
swipeLayoutListener.onOpen(this);
} else if (status == Status.Draging) {
if (preStatus == Status.Close) {
swipeLayoutListener.onStartOpen(this);
} else if (preStatus == Status.Open) {
swipeLayoutListener.onStartClose(this);
}
}
}
} private Status updateStatus() { int left = mFrontView.getLeft();
if (left == 0) {
return Status.Close;
} else if (left == -mRange) {
return Status.Open;
}
return Status.Draging;
} public void close() {
// TODO Auto-generated method stub
DensityUtil.showToast(getContext(), "Close");
close(true);
} public void close(boolean isSmooth) {
// TODO Auto-generated method stub
int finalLeft = 0;
if (isSmooth) {
// 开始动画
if (mDragHelper.smoothSlideViewTo(mFrontView, finalLeft, 0)) {
ViewCompat.postInvalidateOnAnimation(this);
}
} else {
layoutContent(false);
}
} public void open() {
DensityUtil.showToast(getContext(), "Open");
open(true);
} public void open(boolean isSmooth) {
int finalLeft = -mRange;
if (isSmooth) {
// 开始动画
if (mDragHelper.smoothSlideViewTo(mFrontView, finalLeft, 0)) {
ViewCompat.postInvalidateOnAnimation(this);
}
} else {
layoutContent(true);
}
} @Override
public void computeScroll() {
// TODO Auto-generated method stub
super.computeScroll(); if (mDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
} @Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
try {
mDragHelper.processTouchEvent(event);
} catch (Exception e) {
e.printStackTrace();
}
return true;
} @Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
// TODO Auto-generated method stub
super.onLayout(changed, left, top, right, bottom);
// 摆放位置
layoutContent(false);
} private void layoutContent(boolean isOpen) {
// TODO Auto-generated method stub
// 摆放钱View
Rect frontRect = computeFrontViewRect(isOpen);
mFrontView.layout(frontRect.left, frontRect.top, frontRect.right,
frontRect.bottom);
// 摆放后View
Rect backRect = computeBackViewViaFront(frontRect);
mBackView.layout(backRect.left, backRect.top, backRect.right,
backRect.bottom); // 调整顺序,把mFrontView前置
bringChildToFront(mFrontView);
} private Rect computeBackViewViaFront(Rect frontRect) {
// TODO Auto-generated method stub
int left = frontRect.right;
return new Rect(left, 0, left + mRange, 0 + mHeight);
} private Rect computeFrontViewRect(boolean isOpen) {
// TODO Auto-generated method stub
int left = 0;
if (isOpen) {
left = -mRange;
}
return new Rect(left, 0, left + mWidth, 0 + mHeight);
} @Override
protected void onFinishInflate() {
// TODO Auto-generated method stub
super.onFinishInflate();
mBackView = getChildAt(0);
mFrontView = getChildAt(1);
} @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// TODO Auto-generated method stub
super.onSizeChanged(w, h, oldw, oldh);
mHeight = mFrontView.getMeasuredHeight();
mWidth = mFrontView.getMeasuredWidth(); mRange = mBackView.getMeasuredWidth();
} }
package com.demo.sb.adapter; import java.util.ArrayList; import com.demo.sb.adapter.SwipeLayout.OnSwipeLayoutListener;
import com.demo.sb.utils.DensityUtil;
import com.demo.suibian.R; import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView; public class ParallacAdapter extends BaseAdapter { private Context context;
private int[] cost = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
private String[] nameStrings = { "宋江", "卢俊义", "吴用", "公孙胜", "关胜", "林冲",
"秦明", "呼延灼", "花荣", "柴进", "李应", "鲁智深", "索超", "戴宗" };
private ArrayList<SwipeLayout> opendItems; public ParallacAdapter(Context context) {
super();
this.context = context; opendItems = new ArrayList<SwipeLayout>();
} @Override
public int getCount() {
// TODO Auto-generated method stub
return nameStrings.length;
} @Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return nameStrings[arg0];
} @Override
public long getItemId(int arg0) {
// TODO Auto-generated method stub
return arg0;
} @Override
public View getView(final int arg0, View arg1, ViewGroup arg2) {
// TODO Auto-generated method stub
ViewHolder holder;
if (arg1 == null) {
holder = new ViewHolder();
arg1 = View.inflate(context, R.layout.item_parallac_list, null);
holder.tv_del = (TextView) arg1.findViewById(R.id.tv_item_pardel);
holder.tv_name = (TextView) arg1
.findViewById(R.id.item_parallax_name);
holder.tv_fight = (TextView) arg1
.findViewById(R.id.item_parallax_fight);
holder.tv_call = (TextView) arg1.findViewById(R.id.tv_item_parcall);
arg1.setTag(holder);
} else {
holder = (ViewHolder) arg1.getTag();
} SwipeLayout sLayout = (SwipeLayout) arg1;
sLayout.setSwipeLayoutListener(new OnSwipeLayoutListener() { @Override
public void onStartOpen(SwipeLayout mSwipeLayout) {
// TODO Auto-generated method stub
// 要去开启时,先遍历所有已打开的条目,逐个关闭
for (SwipeLayout layout : opendItems) {
layout.close();
}
opendItems.clear();
} @Override
public void onStartClose(SwipeLayout mSwipeLayout) {
// TODO Auto-generated method stub } @Override
public void onOpen(SwipeLayout mSwipeLayout) {
// TODO Auto-generated method stub
opendItems.add(mSwipeLayout);
} @Override
public void onDraging(SwipeLayout mSwipeLayout) {
// TODO Auto-generated method stub } @Override
public void onClose(SwipeLayout mSwipeLayout) {
// TODO Auto-generated method stub
opendItems.remove(mSwipeLayout);
}
});
holder.tv_name.setText("名字为 :" + nameStrings[arg0]);
holder.tv_fight.setText("战斗力: " + cost[arg0]);
holder.tv_del.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View view) {
// TODO Auto-generated method stub
DensityUtil.showToast(context, "第" + arg0 + "个 为"
+ nameStrings[arg0]);
}
});
holder.tv_call.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View view) {
// TODO Auto-generated method stub
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("标题!!!");
builder.setCancelable(false);
builder.setMessage("设置警告,是否确定删除,删除是不可逆的奥");
builder.setPositiveButton("确定",
new DialogInterface.OnClickListener() { @Override
public void onClick(DialogInterface perent, int arg1) {
// TODO Auto-generated method stub
DensityUtil.showToast(context,
nameStrings[arg0]);
}
});
builder.setNegativeButton("取消",
new DialogInterface.OnClickListener() { @Override
public void onClick(DialogInterface arg0, int arg1) {
// TODO Auto-generated method stub }
});
builder.create().show();
}
});
return arg1;
} static class ViewHolder {
TextView tv_del;
TextView tv_name;
TextView tv_fight;
TextView tv_call;
}
}
<?xml version="1.0" encoding="utf-8"?>
<com.demo.sb.adapter.SwipeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/sl"
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="#4000" > <LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal" > <TextView
android:id="@+id/tv_item_parcall"
android:layout_width="100dp"
android:layout_height="match_parent"
android:background="#666"
android:gravity="center"
android:text="call"
android:textColor="#fff" /> <TextView
android:id="@+id/tv_item_pardel"
android:layout_width="100dp"
android:layout_height="match_parent"
android:background="#f00"
android:gravity="center"
android:text="Delete"
android:textColor="#fff" />
</LinearLayout> <LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#4fff"
android:padding="5dp" > <ImageView
android:id="@+id/item_view"
android:layout_width="80dp"
android:layout_height="80dp"
android:contentDescription="@null"
android:scaleType="fitXY"
android:src="@drawable/a" /> <LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp" > <TextView
android:id="@+id/item_parallax_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#f00"
android:textSize="20sp" /> <TextView
android:id="@+id/item_parallax_fight"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:text=""
android:textColor="#5000"
android:textSize="16sp" />
</LinearLayout>
</LinearLayout> </com.demo.sb.adapter.SwipeLayout>
QQ视差特效和ListView侧滑删除的更多相关文章
- QQ 特效学习 二 侧滑删除
上篇文章: http://www.cnblogs.com/xurui1995/p/5798631.html 今天来写不仅是qq而且在别的软件上也特别流行的侧滑删除 其实套路和前篇的一样,一个自定义Vi ...
- listview侧滑删除
自定义Listview,向左滑动,右边刚好显示删除按钮: public class SlideListView extends ListView { private int mScreenWidth; ...
- 史上最简单,一步集成侧滑(删除)菜单,高仿QQ、IOS。
重要的话 开头说,not for the RecyclerView or ListView, for the Any ViewGroup. 本控件不依赖任何父布局,不是针对 RecyclerView. ...
- Android 自定义ListView Item侧滑删除
本程序是基于网上开源项目修改而来,具体来源忘了,懒得搜了,如果有不合适的地方,请原作者联系我,我会及时回复和处理的! 该例子程序中主要包含两个ListView,一个是实现侧滑删除,一个是侧滑出菜单,代 ...
- 9.视差特效、回弹动画、overScrollBy
视差特效 * 应用场景: 微信朋友圈, QQ空间, 微博个人展示,向下拉出,松开回弹* 功能实现: > 1. 重写overScrollBy > 2. 松手之后执行动画, 类型估值器 . ...
- ListView滑动删除效果实现
通过继承ListView然后结合PopupWindow实现 首先是布局文件: delete_btn.xml:这里只需要一个Button <?xml version="1.0" ...
- 记一个SwipeMenuListView侧滑删除错乱的Bug
做侧滑删除网上有很多方案,比如重写Listview实现滑动的监听,今天说下一个SwipeListView,这个是之前一个朋友在网上开源的一个封装组件,能够适用于多种情况,项目地址:https://gi ...
- Android实现RecyclerView侧滑删除和长按拖拽-ItemTouchHelper
RecyclerView这个被誉为ListView和GirdView的替代品,它的用法在之前的一篇博文中就已经讲过了,今天我们就来实现RecyclerView的侧滑删除和长按拖拽功能,实现这两个功能我 ...
- android--------ListView和ExpandableListView的侧滑删除操作
本案例主要实现了ListView和ExpandableListView的侧滑删除操作功能 效果图: ListView的Adapter类 private class SlideAdapter exten ...
随机推荐
- PS 如何改变一个icon的颜色
好吧,码农被逼到一定程度也会自己出手的. PS:如何改变图标颜色 新建一个图层,设置前景色为你想要的前景色,按Alt+Delete键用前景色填充该图层,将该图层拖到这个图片所在图层的上方,按Ctrl+ ...
- 修改windows密码后ssrs报错
昨夜修改了windows的登录密码,第二日发现ssrs全部无法访问.显示filenotfound等错误.细想一下,应该是修改了windows的密码导致ssrs权限验证失败. 因此将ssrs的服务帐号修 ...
- table变宽格式
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- kail2在虚拟机上的安装
首先先要安装虚拟机,打开安装包,下一步 选择典型 选择要安装到的目录,点下一步 4 输入密钥,下一步(密钥网上有很多我这边就例举一个,没用的话就自己找.我这个密钥是VM11 ...
- sql server 2008安装过程中服务器配置出错
请选择:对所有 SQL Server 服务使用相同的账号: 但这里不能给他设置自己定义的账号和密码 ( 例如下面设置它的账号为 sa ,密码为 654321) 就会发生错误:
- Web API 入门系列- 从一个示例开始
1.新建Web API项目 2.新建bookModel 3.新建book web api 控制器 为了简单方便,演示系统使用内存集合持久化书籍. 4.测试web api 我们怎么方便测试web api ...
- asp.net 客户端上传文件全路径获取方法
asp.net 获取客户端上传文件全路径方法: eg:F:\test\1.doc 基于浏览器安全问题,浏览器将屏蔽获取客户端文件全路径的方法,只能获取到文件的文件名,如果需要获取全路径则需要另想其他 ...
- tomcat6 使用comet衍生出的两个额外问题
开发了一个轮询推送功能,网上也有很多文章讲这个就不说怎么做的了.现在发现两个问题: 一:就是登录进主页面后,由于浏览器在不停轮询,导致后端认为前端一直在操作,而正常设定的session超时就跳转到登录 ...
- Google Kubernetes设计文档之服务篇-转
摘要:Kubernetes是Google开源的容器集群管理系统,构建于Docker之上,为容器化的应用提供资源调度.部署运行.服务发现.扩容缩容等功能. Pod是创建.调度和管理的最小部署单位,本文详 ...
- Bucky – 免费开源的实时用户监控工具
Bucky 是一个开源的实时用户监控工具,用于衡量用户在浏览器中使用 Web 应用程序时的性能.它可以自动测量你的网页需要多长时间来加载,Ajax 请求需要多长时间和各个函数需要实行多久. 您可能感兴 ...