前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >京东Java架构师讲解购物车的原理及Java实现

京东Java架构师讲解购物车的原理及Java实现

原创
作者头像
美的让人心动
发布于 2018-06-07 13:19:21
发布于 2018-06-07 13:19:21
2.2K00
代码可运行
举报
文章被收录于专栏:Java架构Java架构
运行总次数:0
代码可运行

今天来写一下关于购物车的东西, 这里首先抛出四个问题:

1)用户没登陆用户名和密码,添加商品, 关闭浏览器再打开后 不登录用户名和密码 问:购物车商品还在吗? 

2)用户登陆了用户名密码,添加商品,关闭浏览器再打开后 不登录用户名和密码 问:购物车商品还在吗?   

3)用户登陆了用户名密码,添加商品, 关闭浏览器,然后再打开,登陆用户名和密码  问:购物车商品还在吗?

4)用户登陆了用户名密码,添加商品, 关闭浏览器 外地老家打开浏览器  登陆用户名和密码 问:购物车商品还在吗?

上面四个问题都是以京东为模板, 那么大家猜猜结果是什么呢?

1)在

2)不在了

3)在

4)在 如果你能够猜到答案, 那么说明你真的很棒, 那么关于这四点是怎么实现的呢? (如果有不认可的小伙伴可以用京东实验一下)

下面我们就来讲解下购物车的原理,最后再来说下具体的code实现.

1)用户没有登录, 添加商品, 此时的商品是被添加到了浏览器的Cookie中, 所以当再次访问时(不登录),商品仍然在Cookie中, 所以购物车中的商品还是存在的.

2)用户登录了,添加商品, 此时会将Cookie中和用户选择的商品都添加到购物车中, 然后删除Cookie中的商品. 所以当用户再次访问(不登录),此时Cookie中的购物车商品已经被删除了, 所以此时购物车中的商品不在了.

3)用户登录, 添加商品,此时商品被添加到数据库做了持久化存储, 再次打开登录用户名和密码, 该用户选择的商品肯定还是存在的, 所以购物车中的商品还是存在的.

4)理由3)

这里再说下 没登录 保存商品到Cookie的优点以及保存到Session和数据库的对比:

1:Cookie: 优点: 保存用户浏览器(不用浪费我们公司的服务器) 缺点:Cookie禁用,不提供保存

2:Session:(Redis : 浪费大量服务器内存:实现、禁用Cookie)  速度很快

3:数据库(Mysql、Redis、SOlr)  能持久化的就数据库  速度太慢

那么我今天要讲的就是:

  • 用户没登陆:购物车添加到Cookie中
  • 用户登陆: 保存购物车到Redis中  (不用数据库)

整体的思路图解:

接下来就是代码实例来实现 购物车的功能了:

首先我们看下购物车和购物项两个JavaBean的设计:

购物车: buyerCart.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 public class BuyerCart implements Serializable{
2 
3     /**
4      * 购物车
5      */
6     private static final long serialVersionUID = 1L;
7     
8     //商品结果集
9     private List<BuyerItem> items = new ArrayList<BuyerItem>();
10     
11     //添加购物项到购物车
12     public void addItem(BuyerItem item){
13         //判断是否包含同款
14         if (items.contains(item)) {
15             //追加数量
16             for (BuyerItem buyerItem : items) {
17                 if (buyerItem.equals(item)) {
18                     buyerItem.setAmount(item.getAmount() + buyerItem.getAmount());
19                 }
20             }
21         }else {
22             items.add(item);
23         }
24         
25     }
26 
27     public List<BuyerItem> getItems() {
28         return items;
29     }
30 
31     public void setItems(List<BuyerItem> items) {
32         this.items = items;
33     }
34     
35     
36     //小计
37     //商品数量
38     @JsonIgnore
39     public Integer getProductAmount(){
40         Integer result = 0;
41         //计算
42         for (BuyerItem buyerItem : items) {
43             result += buyerItem.getAmount();
44         }
45         return result;
46     }
47     
48     //商品金额
49     @JsonIgnore
50     public Float getProductPrice(){
51         Float result = 0f;
52         //计算
53         for (BuyerItem buyerItem : items) {
54             result += buyerItem.getAmount()*buyerItem.getSku().getPrice();
55         }
56         return result;
57     }
58     
59     //运费
60     @JsonIgnore
61     public Float getFee(){
62         Float result = 0f;
63         //计算
64         if (getProductPrice() < 79) {
65             result = 5f;
66         }
67         
68         return result;
69     }
70     
71     //总价
72     @JsonIgnore
73     public Float getTotalPrice(){
74         return getProductPrice() + getFee();
75     }
76     
77 }

这里使用了@JsonIgonre注解是因为下面需要将BuyerCart 转换成Json格式, 而这几个字段只有get 方法, 所以不能转换, 需要使用忽略Json.

下面是购物项: buyerItem.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 public class BuyerItem implements Serializable{
2 
3     private static final long serialVersionUID = 1L;
4 
5     //SKu对象
6     private Sku sku;
7     
8     //是否有货
9     private Boolean isHave = true;
10     
11     //购买的数量
12     private Integer amount = 1;
13 
14     public Sku getSku() {
15         return sku;
16     }
17 
18     public void setSku(Sku sku) {
19         this.sku = sku;
20     }
21 
22     public Boolean getIsHave() {
23         return isHave;
24     }
25 
26     public void setIsHave(Boolean isHave) {
27         this.isHave = isHave;
28     }
29 
30     public Integer getAmount() {
31         return amount;
32     }
33 
34     public void setAmount(Integer amount) {
35         this.amount = amount;
36     }
37 
38     @Override
39     public int hashCode() {
40         final int prime = 31;
41         int result = 1;
42         result = prime * result + ((sku == null) ? 0 : sku.hashCode());
43         return result;
44     }
45 
46     @Override
47     public boolean equals(Object obj) {
48         if (this == obj) //比较地址
49             return true;
50         if (obj == null)
51             return false;
52         if (getClass() != obj.getClass())
53             return false;
54         BuyerItem other = (BuyerItem) obj;
55         if (sku == null) {
56             if (other.sku != null)
57                 return false;
58         } else if (!sku.getId().equals(other.sku.getId()))
59             return false;
60         return true;
61     }
62 }

1、将商品加入购物车中

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 //加入购物车
2 function  addCart(){
3       //  + skuId
4       window.location.href="/shopping/buyerCart?skuId="+skuId+"&amount="+$("#buy-num").val();
5 }

这里传入的参数是skuId(库存表的主键, 库存表保存的商品id,颜色,尺码,库存等信息), 购买数量amount.

接着我们来看Controller是如何来处理的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 //加入购物车
2     @RequestMapping(value="/shopping/buyerCart")
3     public <T> String buyerCart(Long skuId, Integer amount, HttpServletRequest request,
4             HttpServletResponse response) throws JsonParseException, JsonMappingException, IOException{
5         //将对象转换成json字符串/json字符串转成对象
6         ObjectMapper om = new ObjectMapper();
7         om.setSerializationInclusion(Include.NON_NULL);
8         BuyerCart buyerCart = null;
9         //1,获取Cookie中的购物车
10         Cookie[] cookies = request.getCookies();
11         if (null != cookies && cookies.length > 0) {
12             for (Cookie cookie : cookies) {
13                 //
14                 if (Constants.BUYER_CART.equals(cookie.getName())) {
15                     //购物车 对象 与json字符串互转
16                     buyerCart = om.readValue(cookie.getValue(), BuyerCart.class);
17                     break;
18                 }
19             }
20         }
21         
22         //2,Cookie中没有购物车, 创建购物车对象
23         if (null == buyerCart) {
24             buyerCart = new BuyerCart();
25         }
26         
27         //3, 将当前款商品追加到购物车
28         if (null != skuId && null != amount) {
29             Sku sku = new Sku();
30             sku.setId(skuId);
31             BuyerItem buyerItem = new BuyerItem();
32             buyerItem.setSku(sku);
33             //设置数量
34             buyerItem.setAmount(amount);
35             //添加购物项到购物车
36             buyerCart.addItem(buyerItem);
37         }
38         
39         //排序  倒序
40         List<BuyerItem> items = buyerCart.getItems();
41         Collections.sort(items, new Comparator<BuyerItem>() {
42 
43             @Override
44             public int compare(BuyerItem o1, BuyerItem o2) {
45                 return -1;
46             }
47             
48         });
49         
50         //前三点 登录和非登录做的是一样的操作, 在第四点需要判断
51         String username = sessionProviderService.getAttributterForUsername(RequestUtils.getCSessionId(request, response));
52         if (null != username) {
53             //登录了
54             //4, 将购物车追加到Redis中
55             cartService.insertBuyerCartToRedis(buyerCart, username);
56             //5, 清空Cookie 设置存活时间为0, 立马销毁
57             Cookie cookie = new Cookie(Constants.BUYER_CART, null);
58             cookie.setPath("/");
59             cookie.setMaxAge(-0);
60             response.addCookie(cookie);
61         }else {
62             //未登录
63             //4, 保存购物车到Cookie中
64             //将对象转换成json格式
65             Writer w = new StringWriter();
66             om.writeValue(w, buyerCart);
67             Cookie cookie = new Cookie(Constants.BUYER_CART, w.toString());
68             //设置path是可以共享cookie
69             cookie.setPath("/");
70             //设置Cookie过期时间: -1 表示关闭浏览器失效  0: 立即失效  >0: 单位是秒, 多少秒后失效
71             cookie.setMaxAge(24*60*60);
72             //5,Cookie写会浏览器
73             response.addCookie(cookie);
74         }
75         
76         //6, 重定向
77         return "redirect:/shopping/toCart";
78     }

这里设计一个知识点: 将对象转换成json字符串/json字符串转成对象

我们在这里先写一个小的Demo来演示json和对象之间的互转, 这里使用到了springmvc中的ObjectMapper类.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 public class TestJson {
2 
3     @Test
4     public void testAdd() throws Exception {
5         TestTb testTb = new TestTb();
6         testTb.setName("范冰冰");
7         ObjectMapper om = new ObjectMapper();
8         om.setSerializationInclusion(Include.NON_NULL);
9         //将对象转换成json字符串
10         Writer wr = new StringWriter();
11         om.writeValue(wr, testTb);
12         System.out.println(wr.toString());
13         
14         //转回对象
15         TestTb r = om.readValue(wr.toString(), TestTb.class);
16         System.out.println(r.toString());
17     }
18     
19 }

执行结果: 

这里我们使用了Include.NON_NULL, 如果TestTb 中属性为null 的就不给转换成Json, 从对象-->Json字符串  用的是 objectMapper.writeValue(). 从Json字符串-->对象使用的是objectMapper.readValue(). 回归上面我们项目中的代码, 只有未登录 添加商品时才会将此商品添加到Cookie中.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 //未登录
2             //4, 保存购物车到Cookie中
3             //将对象转换成json格式
4             Writer w = new StringWriter();
5             om.writeValue(w, buyerCart);
6             Cookie cookie = new Cookie(Constants.BUYER_CART, w.toString());
7             //设置path是可以共享cookie
8             cookie.setPath("/");
9             //设置Cookie过期时间: -1 表示关闭浏览器失效  0: 立即失效  >0: 单位是秒, 多少秒后失效
10             cookie.setMaxAge(24*60*60);
11             //5,Cookie写会浏览器
12             response.addCookie(cookie);

我们debug 可以看到:

这里已经将对象购物车对象buyerCart转换成了Json格式.

将商品添加到购物车, 不管是登录还是未登录, 都要先取出Cookie中的购物车, 然后将当前选择的商品追加到购物车中.

然后登录的话  就把Cookie中的购物车清空, 并将购物车的内容添加到Redis中做持久化保存.

如果未登录, 将选择的商品追加到Cookie中.

将购物车追加到Redis中的代码:insertBuyerCartToRedis(这里面包含了判断添加的是否是同款)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 //保存购物车到Redis中
2     public void insertBuyerCartToRedis(BuyerCart buyerCart, String username){
3         List<BuyerItem> items = buyerCart.getItems();
4         if (items.size() > 0) {
5             //redis中保存的是skuId 为key , amount 为value的Map集合
6             Map<String, String> hash = new HashMap<String, String>();
7             for (BuyerItem item : items) {
8                 //判断是否有同款
9                 if (jedis.hexists("buyerCart:"+username, String.valueOf(item.getSku().getId()))) {
10                     jedis.hincrBy("buyerCart:"+username, String.valueOf(item.getSku().getId()), item.getAmount());
11                 }else {
12                     hash.put(String.valueOf(item.getSku().getId()), String.valueOf(item.getAmount()));
13                 }
14             }
15             if (hash.size() > 0) {
16                 jedis.hmset("buyerCart:"+username, hash);
17             }
18         }
19         
20     }

判断用户是否登录: String username =

sessionProviderService.getAttributterForUsername(RequestUtils.getCSessionId(request, response));

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 public class RequestUtils {
2 
3     //获取CSessionID
4     public static String getCSessionId(HttpServletRequest request, HttpServletResponse response){
5         //1, 从Request中取Cookie
6         Cookie[] cookies = request.getCookies();
7         //2, 从Cookie数据中遍历查找, 并取CSessionID
8         if (null != cookies && cookies.length > 0) {
9             for (Cookie cookie : cookies) {
10                 if ("CSESSIONID".equals(cookie.getName())) {
11                     //有, 直接返回
12                     return cookie.getValue();
13                 }
14             }
15         }
16         //没有, 创建一个CSessionId, 并且放到Cookie再返回浏览器.返回新的CSessionID
17         String csessionid = UUID.randomUUID().toString().replaceAll("-", "");
18         //并且放到Cookie中
19         Cookie cookie = new Cookie("CSESSIONID", csessionid);
20         //cookie  每次都带来, 设置路径
21         cookie.setPath("/");
22         //0:关闭浏览器  销毁cookie. 0:立即消失.  >0 存活时间,秒
23         cookie.setMaxAge(-1);
24         
25         return csessionid;
26     }
27 }
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 //获取
2     public String getAttributterForUsername(String jessionId){
3         String value = jedis.get(jessionId + ":USER_NAME");
4         if(null != value){
5             //计算session过期时间是 用户最后一次请求开始计时.
6             jedis.expire(jessionId + ":USER_NAME", 60*exp);
7             return value;
8         }
9         return null;
10     }

2、购物车展示页面

最后 重定向到购物车展示页: return "redirect:/shopping/toCart"; 这里进入结算页有两种方式:

1) 在商品详情页 点击加入购物车.

2) 直接点击购物车按钮 进入购物车结算页.

下面来看下结算页的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 @Autowired
2     private CartService cartService;
3     //去购物车结算, 这里有两个地方可以直达: 1,在商品详情页 中点击加入购物车按钮  2, 直接点击购物车按钮
4     @RequestMapping(value="/shopping/toCart")
5     public String toCart(Model model, HttpServletRequest request,
6             HttpServletResponse response) throws JsonParseException, JsonMappingException, IOException{ 
7         //将对象转换成json字符串/json字符串转成对象
8         ObjectMapper om = new ObjectMapper();
9         om.setSerializationInclusion(Include.NON_NULL);
10         BuyerCart buyerCart = null;
11         //1,获取Cookie中的购物车
12         Cookie[] cookies = request.getCookies();
13         if (null != cookies && cookies.length > 0) {
14             for (Cookie cookie : cookies) {
15                 //
16                 if (Constants.BUYER_CART.equals(cookie.getName())) {
17                     //购物车 对象 与json字符串互转
18                     buyerCart = om.readValue(cookie.getValue(), BuyerCart.class);
19                     break;
20                 }
21             }
22         }
23         
24         //判断是否登录
25         String username = sessionProviderService.getAttributterForUsername(RequestUtils.getCSessionId(request, response));
26         if (null != username) {
27             //登录了
28             //2, 购物车 有东西, 则将购物车的东西保存到Redis中
29             if (null == buyerCart) {
30                 cartService.insertBuyerCartToRedis(buyerCart, username);
31                 //清空Cookie 设置存活时间为0, 立马销毁
32                 Cookie cookie = new Cookie(Constants.BUYER_CART, null);
33                 cookie.setPath("/");
34                 cookie.setMaxAge(-0);
35                 response.addCookie(cookie);
36             }
37             //3, 取出Redis中的购物车
38             buyerCart = cartService.selectBuyerCartFromRedis(username);
39         }
40         
41         
42         //4, 没有 则创建购物车
43         if (null == buyerCart) {
44             buyerCart = new BuyerCart();
45         }
46         
47         //5, 将购物车装满, 前面只是将skuId装进购物车, 这里还需要查出sku详情
48         List<BuyerItem> items = buyerCart.getItems();
49         if(items.size() > 0){
50             //只有购物车中有购物项, 才可以将sku相关信息加入到购物项中
51             for (BuyerItem buyerItem : items) {
52                 buyerItem.setSku(cartService.selectSkuById(buyerItem.getSku().getId()));
53             }
54         }
55         
56         //5,上面已经将购物车装满了, 这里直接回显页面
57         model.addAttribute("buyerCart", buyerCart);
58         
59         //跳转购物页面
60         return "cart";
61     }

这里 就是 购物车详情展示页面, 这里需要注意, 如果是同一件商品连续添加, 是需要合并的.

购物车详情展示页面就包括两大块, 1) 商品详情 2)总计(商品总额,运费)

其中1)商品详情又包括 商品尺码,商品颜色, 商品购买数量, 是否有货.

取出Redis中的购物车: buyerCart = cartService.selectBuyerCartFromRedis(username);

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1     //取出Redis中购物车
2     public BuyerCart selectBuyerCartFromRedis(String username){
3         BuyerCart buyerCart = new BuyerCart();
4         //获取所有商品, redis中保存的是skuId 为key , amount 为value的Map集合
5         Map<String, String> hgetAll = jedis.hgetAll("buyerCart:"+username);
6         Set<Entry<String, String>> entrySet = hgetAll.entrySet();
7         for (Entry<String, String> entry : entrySet) {
8             //entry.getKey(): skuId
9             Sku sku = new Sku();
10             sku.setId(Long.parseLong(entry.getKey()));
11             BuyerItem buyerItem = new BuyerItem();
12             buyerItem.setSku(sku);
13             //entry.getValue(): amount
14             buyerItem.setAmount(Integer.parseInt(entry.getValue()));
15             //添加到购物车中
16             buyerCart.addItem(buyerItem);
17         }
18         
19         return buyerCart;
20     }

将购物车装满, 前面只是将skuId装进购物车, 这里还需要查出sku详情: List<BuyerItem> items = buyerCart.getItems(); buyerItem.setSku(cartService.selectSkuById(buyerItem.getSku().getId()));

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 //向购物车中的购物项 添加相应的数据, 通过skuId 查询sku对象, 颜色对象, 商品对象
2     public Sku selectSkuById(Long skuId){
3         Sku sku = skuDao.selectByPrimaryKey(skuId);
4         //颜色
5         sku.setColor(colorDao.selectByPrimaryKey(sku.getColorId()));
6         //添加商品信息
7         sku.setProduct(productDao.selectByPrimaryKey(sku.getProductId()));
8         return sku;
9     }

接着就返回"cart.jsp", 这个就是购物车详情展示页面了.

3、去结算页面

到了这里就说明用户必须要 登录, 而且购物车中必须要有商品.

所以这里我么你需要利用springmvc的过滤功能, 用户点击结算的时候必须要先登录, 如果没有登录的话就提示用户需要登录.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 //去结算
2     @RequestMapping(value="/buyer/trueBuy")
3     public String trueBuy(String[] skuIds, Model model, HttpServletRequest request, HttpServletResponse response){
4         //1, 购物车必须有商品, 
5         //取出用户名  再取出购物车
6         String username = sessionProviderService.getAttributterForUsername(RequestUtils.getCSessionId(request, response));
7         //取出所有购物车
8         BuyerCart buyerCart = cartService.selectBuyerCartFromRedisBySkuIds(skuIds, username);
9         List<BuyerItem> items = buyerCart.getItems();
10         if (items.size() > 0) {
11             //购物车中有商品
12             //判断所勾选的商品是否都有货, 如果有一件无货, 那么就刷新页面.
13             Boolean flag = true;
14             //2, 购物车中商品必须有库存 且购买大于库存数量时视为无货. 提示: 购物车原页面不动. 有货改为无货, 加红提醒.
15             for (BuyerItem buyerItem : items) {
16                 //装满购物车的购物项, 当前购物项只有skuId这一个东西, 我们还需要购物项的数量去判断是否有货
17                 buyerItem.setSku(cartService.selectSkuById(buyerItem.getSku().getId()));
18                 //校验库存
19                 if (buyerItem.getAmount() > buyerItem.getSku().getStock()) {
20                     //无货
21                     buyerItem.setIsHave(false);
22                     flag = false;
23                 }
24                 if (!flag) {
25                     //无货, 原页面不动, 有货改成无货, 刷新页面.
26                     model.addAttribute("buyerCart", buyerCart);
27                     return "cart";
28                 }
29             }
30         }else {
31             //购物车没有商品
32             //没有商品: 1>原购物车页面刷新(购物车页面提示没有商品)
33             return "redirect:/shopping/toCart";
34         }
35         
36         
37         //3, 正常进入下一个页面
38         return "order";
39     }

取出 所指定的购物车, 因为我们结算之前在购物车详情页面会勾选 我们 需要购买的商品, 所以这里是根据所勾选的商品去结算的. BuyerCart buyerCart = cartService.selectBuyerCartFromRedisBySkuIds(skuIds, username);

从购物车中取出指定商品:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1 //从购物车中取出指定商品
2     public BuyerCart selectBuyerCartFromRedisBySkuIds(String[] skuIds, String username){
3         BuyerCart buyerCart = new BuyerCart();
4         //获取所有商品, redis中保存的是skuId 为key , amount 为value的Map集合
5         Map<String, String> hgetAll = jedis.hgetAll("buyerCart:"+username);
6         if (null != hgetAll && hgetAll.size() > 0) {
7             Set<Entry<String, String>> entrySet = hgetAll.entrySet();
8             for (Entry<String, String> entry : entrySet) {
9                 for (String skuId : skuIds) {
10                     if (skuId.equals(entry.getKey())) {
11                         //entry.getKey(): skuId
12                         Sku sku = new Sku();
13                         sku.setId(Long.parseLong(entry.getKey()));
14                         BuyerItem buyerItem = new BuyerItem();
15                         buyerItem.setSku(sku);
16                         //entry.getValue(): amount
17                         buyerItem.setAmount(Integer.parseInt(entry.getValue()));
18                         //添加到购物车中
19                         buyerCart.addItem(buyerItem);
20                     }
21                 }
22             }
23         }
24         
25         return buyerCart;
26     }

1) 当我们购买的商品只要有一件是无货的状态, 那么刷新购物车详情页面, 回显无货的商品状态. 

2)当购物车中午商品时, 刷新当前页面.

购物车就这么多东西, 可能有讲解不到或者错误的地方, 欢迎大家指出来.如果对你有帮助的话也请点个赞支持一下,谢谢~

我有一个群,经常会分享一些Java技术相关的干货;如果你喜欢我的分享,

Java架构/分布式:685167672(大牛交流群)没有开发经验勿加!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
窗口置顶工具v2.4.0
很高兴能更新到2.4.0版本。 小君在coding时,有时候需要快捷置顶,但又不想移动鼠标到置顶栏操作,于是想到点击几次鼠标置顶的操作,偷偷懒,至于按压窗口空白处移动窗口,同样也是不想移动到标题栏点击移动,倒是点点鼠标移动就可以移动窗口了,多好啊。
Qt君
2023/03/17
6150
窗口置顶工具v2.4.0
发布窗口置顶工具1.0.0
  一个Windows平台的窗口置顶工具。目前添加了以下功能: 置顶窗口功能; 窗口穿透功能; 窗口透明度设置功能; 窗口自定义画中画功能。 演示 置顶 2. 鼠标穿透 透明度设置 画中画 拖动效果 访问以下链接: https://github.com/aeagean/WindowTop
Qt君
2023/03/17
7710
发布窗口置顶工具1.0.0
窗口置顶工具v1.2.0
访问以下链接: https://github.com/aeagean/WindowTop
Qt君
2023/03/17
4370
窗口置顶工具v1.2.0
窗口置顶工具v1.1.0
访问以下链接: https://github.com/aeagean/WindowTop
Qt君
2023/03/17
3580
窗口置顶工具v1.1.0
窗口置顶工具v2.7.0(GIF录制)
大家好,很高兴能再次更新版本。本次主要更新是GIF窗口录制的功能,该功能支持最高100帧/秒录制,高压缩比的同时尽可能保持高质量。
Qt君
2023/09/18
1790
窗口置顶工具v2.7.0(GIF录制)
电脑软件:SmartSystemMenu(窗口置顶工具)介绍
SmartSystemMenu 是一款简单实用的 Windows 窗口增强工具,它可以为窗口的标题栏右键菜单新增 17 个新功能。
小明互联网技术分享社区
2023/11/21
7340
电脑软件:SmartSystemMenu(窗口置顶工具)介绍
窗口置顶工具v1.3.0
访问以下链接: https://github.com/aeagean/WindowTop
Qt君
2023/03/17
4410
窗口置顶工具v1.3.0
Camtasia2023最新版使用快捷键教程
使用Camtasia,您可以毫不费力地在计算机的显示器上录制专业的活动视频。除了录制视频外,Camtasia还允许您从外部源将高清视频导入到录制中。Camtasia的独特之处在于它可以创建包含可单击链接的交互式视频,以生成适用于教室或工作场所的动态视频内容。(Win10,Win11 兼容)最近发布了Camtasia2023版本,新增超过130个过滤效果,将标注、文本和其他元素组合在一起,轻松处理大型项目和视频文件。
用户7442547
2023/02/14
1.6K0
BetterDisplay Pro Mac(显示器管理工具)v1.4.7激活版
BetterDisplay Pro Mac版是一款屏幕显示优化工具,可以帮助用户调整屏幕的亮度、对比度、色彩等参数,以获得更好的视觉体验。此外,它还提供了一些额外的功能,如屏幕分割、窗口管理、快捷键设置等,帮助用户更高效地使用电脑。用户可以根据自己的需求进行屏幕参数调整和功能设置,以达到最佳效果。总的来说,BetterDisplay Pro是一款实用的屏幕优化工具,可以为用户带来更好的视觉体验和更高效的电脑使用方式。
小草莓
2023/03/29
1.9K1
BetterDisplay Pro Mac(显示器管理工具)v1.4.7激活版
iOS14功能更新详解,空间音频功能上线!
(VRPinea 9月23日讯)近期举办的苹果秋季新品发布会,也许是近年来最令人失望的苹果发布会,本次发布会不但没有公布新款iPhone12,发布会的内容也极少,只有寥寥几款产品推出。好在苹果还是宣布了一个令熬夜果粉稍稍欣慰的消息——iOS14正式推送。相比于之前的beta版本,iOS14正式版最亮眼的更新,就是实装了传闻中的空间音频技术。
VRPinea
2020/09/30
1.4K0
首个基于西瓜播放器的WordPress m3u8视频播放器插件wp xgplayer
前段时间不是写了基于videojs播放器的插件吗,然后看见有人说国内用DPlayer来搭配的比较多,我就找了下DPlayer,但是找的途中,才看见了西瓜播放器这款产品,是字节跳动旗下的开源产品。所以我就直接放弃DPlayer,改用西瓜播放器来试了下。
速企云
2024/04/21
9775
首个基于西瓜播放器的WordPress m3u8视频播放器插件wp xgplayer
Snpiaste截图软件使用
即上次整理了秀米编辑器使用后,曾老师提议其实可以做个效率工具合集,转念一想小洁老师在马拉松课程里面可是提到了不少提升效率的软件以及网页插件,小助教这就整理起来!
生信技能树
2024/04/26
2830
Snpiaste截图软件使用
那些防不胜防的坑儿
AirPlay:AirPlay 是指将iOS设备或者Mac设备上的音视频,同步到另一个设备中播放。比如:将iPhone上的音乐通过蓝牙的方式在汽车的蓝牙音响上播放。此功能一般用于多端及多屏的交互。 画中画的视频播放:画中画是 iPad 版本的iOS 9新增加的功能,可以在 iOS 的桌面,或者其他应用的界面的上面播放视频,从而该视频区域所属的应用就可以后台运行了。此功能现在只在 iPad 应用中提供。
用户5521279
2019/06/02
1.4K0
国产linux操作系统深度系统20.3发布(推荐)
深度操作系统(deepin)是一个致力于为全球用户提供美观易用、安全稳定服务的Linux发行版,同时也一直是排名最高的来自中国团队研发的Linux发行版。(了解deepin国际排名)
全栈程序员站长
2022/08/29
6K0
国产linux操作系统深度系统20.3发布(推荐)
会声会影2023专业版新功能讲解
多场景适用,会声会影2023适用于个人、商店或是企业,可满足vlog视频、影视混剪、游戏解说、电子相册制作、淘宝主图视频、企业宣传片、线上网课制作等需求!采用优质的效果、强大的工具、可定制的转场,以及新的 AR 动画贴纸和 GIF,制作引人瞩目的视频。通过数百种效果、即时项目模板、标题和转场,加上新的动画 AR 贴纸和 GIF 创建器,探索拖放式创意。下载末尾会声会影教程参考!会声会影2023(Corel VideoStudio Ultimate 2023)(亦称绘声绘影)是一款功能强大的视频编辑软件,入手非常简单,可以帮助用户制作优秀的视频内容,支持视频编辑和视频特效等,是一款普及度非常高的视频编辑软件。它是一款高效的视频剪辑处理软件。该软件在这个版本中增加了全新的AI智能小工具,不论是家庭还是工作中使用都非常的方便,甚至可以挑战专业级的影片,非常适合想要制作视频的人来使用。
用户7442547
2023/01/03
1.5K0
Slidepad for Mac(高效率办公软件)v1.3.9激活版
Slidepad Mac版是Mac平台上的一款高效率办公软件,Slidepad for Mac下载可以在你的Mac电脑上设置一个侧边窗口,类似于iPad的功能,在那里你可以放置你最喜欢的网络应用程序和网站。
小草莓
2022/10/29
4760
Slidepad for Mac(高效率办公软件)v1.3.9激活版
Snipaste:高效便捷的截图工具,提升工作效率的利器
Snipaste 是一个简单但强大的截图工具,可以让截图贴回到屏幕上!下载并打开 Snipaste,按下 F1 来开始截图,再按 F3,截图就在桌面置顶显示了。
M.Talen
2024/05/22
2530
Snipaste:高效便捷的截图工具,提升工作效率的利器
Slidepad:iPad式APP切换工具
Slidepad Mac版是一款强大的iPad式APP切换工具,可以在macos上面发挥iPad即用即走的特点,让网页或者应用就像磁贴一样在侧边栏进行固定,在你需要的时候只需要轻触即可,让你使用起来更加的方便!
啾咪啾咪
2022/10/12
9500
Android Oreo 常见问题 2.0 | Android 开发者 FAQ Vol.9
在第一期 Android Oreo 8.0 开发者 FAQ 中,我们为了尽快让大家快速了解 Android Oreo 的新特性,以及它与之前版本 Android 的区别,我们针对 Android Oreo 发布后收到的大量留言咨询与重要新版本特性所留下了许多有代表性的问题逐个进行了解答。 然而对于 Android Oreo 这样一个全新的重大版本,寥寥几个问题无法代表全部开发者和用户。 近期,随着各个型号的手机陆陆续续升级到 Android Oreo,我们收到了更多的关于 Android Oreo 的留言
Android 开发者
2018/05/31
2K0
腾讯云音视频播放器又上新啦!短视频秒开组件、加密画中画等功能让音视频播放更专业!
根据腾讯云音视频官方的消息显示,播放器SDK是音视频终端SDK的子产品之一,它采用“腾讯视频”同款播放内核,经过内部业务长期优化和海量服务验证,对比系统播放器性能可提升20%-50%,同时具备“臻彩视听”、精准Seek、画中画等丰富功能,为用户提供直播、点播场景下流畅稳定的音视频播放能力,覆盖泛娱乐、电商、教育等多样化音视频业务场景,支持Web/H5、iOS、Android、Flutter平台。
三掌柜
2023/12/29
9324
腾讯云音视频播放器又上新啦!短视频秒开组件、加密画中画等功能让音视频播放更专业!
推荐阅读
相关推荐
窗口置顶工具v2.4.0
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验